/// <summary> /// Method used for handling creature targeted spell casts /// </summary> public void CreateCreatureSpell(ObjectGuid guidTarget, uint spellId) { Creature creature = CurrentLandblock?.GetObject(Guid) as Creature; if (creature.IsBusy == true) { return; } creature.IsBusy = true; SpellTable spellTable = DatManager.PortalDat.SpellTable; if (!spellTable.Spells.ContainsKey(spellId)) { creature.IsBusy = false; return; } SpellBase spell = spellTable.Spells[spellId]; bool targetSelf = false || (int)Math.Floor(spell.BaseRangeConstant) == 0; var target = targetSelf ? this : CurrentLandblock?.GetObject(guidTarget); Database.Models.World.Spell spellStatMod = DatabaseManager.World.GetCachedSpell(spellId); if (spellStatMod == null) { creature.IsBusy = false; return; } float scale = SpellAttributes(null, spellId, out float castingDelay, out MotionCommand windUpMotion, out MotionCommand spellGesture); switch (spell.School) { case MagicSchool.ItemEnchantment: // if (!targetSelf && ResistSpell(Skill.CreatureEnchantment)) break; ItemMagic(target, spell, spellStatMod); EnqueueBroadcast(new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); break; case MagicSchool.LifeMagic: break; case MagicSchool.CreatureEnchantment: break; case MagicSchool.WarMagic: break; } creature.IsBusy = false; return; }
/// <summary> /// Void Magic /// </summary> /// <param name="target"></param> /// <param name="spell"></param> /// <param name="spellStatMod"></param> protected void VoidMagic(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod) { if (WeenieClassId == 1) { Player player = (Player)this; player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.None), new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); } }
/// <summary> /// Item Magic /// </summary> /// <param name="target"></param> /// <param name="spell"></param> /// <param name="spellStatMod"></param> /// <param name="castByItem"></param> protected string ItemMagic(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod, bool castByItem = false) { Player player = CurrentLandblock.GetObject(Guid) as Player; if ((spell.MetaSpellType == SpellType.PortalLink) || (spell.MetaSpellType == SpellType.PortalRecall) || (spell.MetaSpellType == SpellType.PortalSending) || (spell.MetaSpellType == SpellType.PortalSummon)) { switch (spell.MetaSpellId) { case 2645: // Portal Recall if (!player.TeleToPosition(PositionType.LastPortal)) { // You must link to a portal to recall it! player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.YouMustLinkToPortalToRecall)); } break; case 1635: // Lifestone Recall if (!player.TeleToPosition(PositionType.LinkedLifestone)) { // You must link to a lifestone to recall it! player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.YouMustLinkToLifestoneToRecall)); } break; case 48: // Primary Portal Recall if (!player.TeleToPosition(PositionType.LinkedPortalOne)) { // You must link to a portal to recall it! player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.YouMustLinkToPortalToRecall)); } break; case 2647: // Secondary Portal Recall if (!player.TeleToPosition(PositionType.LinkedPortalTwo)) { // You must link to a portal to recall it! player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.YouMustLinkToPortalToRecall)); } break; default: break; } } else if (spell.MetaSpellType == SpellType.Enchantment) { return(CreateEnchantment(target, spell, spellStatMod, castByItem)); } return(""); }
/// <summary> /// War Magic /// </summary> /// <param name="target"></param> /// <param name="spell"></param> /// <param name="spellStatMod"></param> protected void WarMagic(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod) { if (spellStatMod.NumProjectiles == 1) { CreateSpellProjectile(this, target, spell.MetaSpellId, (uint)spellStatMod.Wcid); } else { if (WeenieClassId == 1) { Player player = (Player)this; player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.None), new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); } } }
/// <summary> /// Add/update an enchantment in this object's registry /// </summary> public StackType Add(Enchantment enchantment, string castByItem) { // check for existing spell in this category var entry = GetCategory(enchantment.Spell.Category); // if none, add new record if (entry == null) { entry = BuildEntry(enchantment.Spell.SpellId, castByItem); entry.LayerId = enchantment.Layer; var type = (EnchantmentTypeFlags)entry.StatModType; WorldObject.Biota.BiotaPropertiesEnchantmentRegistry.Add(entry); return(StackType.Initial); } if (enchantment.Spell.Power > entry.PowerLevel) { // surpass existing spell Surpass = DatabaseManager.World.GetCachedSpell((uint)entry.SpellId); Remove(entry, false); entry = BuildEntry(enchantment.Spell.SpellId, castByItem); entry.LayerId = enchantment.Layer; WorldObject.Biota.BiotaPropertiesEnchantmentRegistry.Add(entry); return(StackType.Surpass); } if (enchantment.Spell.Power == entry.PowerLevel) { if (entry.Duration == -1) { return(StackType.None); } // refresh existing spell entry.LayerId++; entry.StartTime = 0; return(StackType.Refresh); } // superior existing spell Surpass = DatabaseManager.World.GetCachedSpell((uint)entry.SpellId); return(StackType.Surpassed); }
public static CreateResult Create(CharacterCreateInfo characterCreateInfo, Weenie weenie, ObjectGuid guid, uint accountId, WeenieType weenieType, out Player player) { var heritageGroup = DatManager.PortalDat.CharGen.HeritageGroups[(uint)characterCreateInfo.Heritage]; if (weenieType == WeenieType.Admin) { player = new Admin(weenie, guid, accountId); } else if (weenieType == WeenieType.Sentinel) { player = new Sentinel(weenie, guid, accountId); } else { player = new Player(weenie, guid, accountId); } player.SetProperty(PropertyInt.HeritageGroup, (int)characterCreateInfo.Heritage); player.SetProperty(PropertyString.HeritageGroup, heritageGroup.Name); player.SetProperty(PropertyInt.Gender, (int)characterCreateInfo.Gender); player.SetProperty(PropertyString.Sex, characterCreateInfo.Gender == 1 ? "Male" : "Female"); //player.SetProperty(PropertyDataId.Icon, cgh.IconImage); // I don't believe this is used anywhere in the client, but it might be used by a future custom launcher // pull character data from the dat file var sex = heritageGroup.Genders[(int)characterCreateInfo.Gender]; player.SetProperty(PropertyDataId.MotionTable, sex.MotionTable); player.SetProperty(PropertyDataId.SoundTable, sex.SoundTable); player.SetProperty(PropertyDataId.PhysicsEffectTable, sex.PhysicsTable); player.SetProperty(PropertyDataId.Setup, sex.SetupID); player.SetProperty(PropertyDataId.PaletteBase, sex.BasePalette); player.SetProperty(PropertyDataId.CombatTable, sex.CombatTable); // Check the character scale if (sex.Scale != 100) { player.SetProperty(PropertyFloat.DefaultScale, sex.Scale / 100.0f); // Scale is stored as a percentage } // Get the hair first, because we need to know if you're bald, and that's the name of that tune! var hairstyle = sex.HairStyleList[(int)characterCreateInfo.Appearance.HairStyle]; // Olthoi and Gear Knights have a "Body Style" instead of a hair style. These styles have multiple model/texture changes, instead of a single head/hairstyle. // Storing this value allows us to send the proper appearance ObjDesc if (hairstyle.ObjDesc.AnimPartChanges.Count > 1) { player.SetProperty(PropertyInt.Hairstyle, (int)characterCreateInfo.Appearance.HairStyle); } // Certain races (Undead, Tumeroks, Others?) have multiple body styles available. This is controlled via the "hair style". if (hairstyle.AlternateSetup > 0) { player.SetProperty(PropertyDataId.Setup, hairstyle.AlternateSetup); } player.SetProperty(PropertyDataId.EyesTexture, sex.GetEyeTexture(characterCreateInfo.Appearance.Eyes, hairstyle.Bald)); player.SetProperty(PropertyDataId.DefaultEyesTexture, sex.GetDefaultEyeTexture(characterCreateInfo.Appearance.Eyes, hairstyle.Bald)); player.SetProperty(PropertyDataId.NoseTexture, sex.GetNoseTexture(characterCreateInfo.Appearance.Nose)); player.SetProperty(PropertyDataId.DefaultNoseTexture, sex.GetDefaultNoseTexture(characterCreateInfo.Appearance.Nose)); player.SetProperty(PropertyDataId.MouthTexture, sex.GetMouthTexture(characterCreateInfo.Appearance.Mouth)); player.SetProperty(PropertyDataId.DefaultMouthTexture, sex.GetDefaultMouthTexture(characterCreateInfo.Appearance.Mouth)); player.Character.HairTexture = sex.GetHairTexture(characterCreateInfo.Appearance.HairStyle) ?? 0; player.Character.DefaultHairTexture = sex.GetDefaultHairTexture(characterCreateInfo.Appearance.HairStyle) ?? 0; // HeadObject can be null if we're dealing with GearKnight or Olthoi var headObject = sex.GetHeadObject(characterCreateInfo.Appearance.HairStyle); if (headObject != null) { player.SetProperty(PropertyDataId.HeadObject, (uint)headObject); } // Skin is stored as PaletteSet (list of Palettes), so we need to read in the set to get the specific palette var skinPalSet = DatManager.PortalDat.ReadFromDat <PaletteSet>(sex.SkinPalSet); player.SetProperty(PropertyDataId.SkinPalette, skinPalSet.GetPaletteID(characterCreateInfo.Appearance.SkinHue)); player.SetProperty(PropertyFloat.Shade, characterCreateInfo.Appearance.SkinHue); // Hair is stored as PaletteSet (list of Palettes), so we need to read in the set to get the specific palette var hairPalSet = DatManager.PortalDat.ReadFromDat <PaletteSet>(sex.HairColorList[(int)characterCreateInfo.Appearance.HairColor]); player.SetProperty(PropertyDataId.HairPalette, hairPalSet.GetPaletteID(characterCreateInfo.Appearance.HairHue)); // Eye Color player.SetProperty(PropertyDataId.EyesPalette, sex.EyeColorList[(int)characterCreateInfo.Appearance.EyeColor]); // skip over this for olthoi, use the weenie defaults if (!player.IsOlthoiPlayer) { 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.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))); } string templateName = heritageGroup.Templates[characterCreateInfo.TemplateOption].Name; player.SetProperty(PropertyString.Template, templateName); player.AddTitle(heritageGroup.Templates[characterCreateInfo.TemplateOption].Title, true); // attributes var result = ValidateAttributeCredits(characterCreateInfo, heritageGroup.AttributeCredits); if (result != CreateResult.Success) { return(result); } player.Strength.StartingValue = characterCreateInfo.StrengthAbility; player.Endurance.StartingValue = characterCreateInfo.EnduranceAbility; player.Coordination.StartingValue = characterCreateInfo.CoordinationAbility; player.Quickness.StartingValue = characterCreateInfo.QuicknessAbility; player.Focus.StartingValue = characterCreateInfo.FocusAbility; player.Self.StartingValue = characterCreateInfo.SelfAbility; // data we don't care about //characterCreateInfo.CharacterSlot; //characterCreateInfo.ClassId; // characters start with max vitals player.Health.Current = player.Health.Base; player.Stamina.Current = player.Stamina.Base; player.Mana.Current = player.Mana.Base; // set initial skill credit amount. 52 for all but "Olthoi", which have 68 player.SetProperty(PropertyInt.AvailableSkillCredits, (int)heritageGroup.SkillCredits); player.SetProperty(PropertyInt.TotalSkillCredits, (int)heritageGroup.SkillCredits); if (characterCreateInfo.SkillAdvancementClasses.Count != 55) { return(CreateResult.ClientServerSkillsMismatch); } for (int i = 0; i < characterCreateInfo.SkillAdvancementClasses.Count; i++) { var sac = characterCreateInfo.SkillAdvancementClasses[i]; if (sac == SkillAdvancementClass.Inactive) { continue; } if (!DatManager.PortalDat.SkillTable.SkillBaseHash.ContainsKey((uint)i)) { log.ErrorFormat("Character {0} tried to create with skill {1} that was not found in Portal dat.", characterCreateInfo.Name, i); return(CreateResult.InvalidSkillRequested); } var skill = DatManager.PortalDat.SkillTable.SkillBaseHash[(uint)i]; var trainedCost = skill.TrainedCost; var specializedCost = skill.UpgradeCostFromTrainedToSpecialized; foreach (var skillGroup in heritageGroup.Skills) { if (skillGroup.SkillNum == i) { trainedCost = skillGroup.NormalCost; specializedCost = skillGroup.PrimaryCost; break; } } if (sac == SkillAdvancementClass.Specialized) { if (!player.TrainSkill((Skill)i, trainedCost)) { return(CreateResult.FailedToTrainSkill); } if (!player.SpecializeSkill((Skill)i, specializedCost)) { return(CreateResult.FailedToSpecializeSkill); } } else if (sac == SkillAdvancementClass.Trained) { if (!player.TrainSkill((Skill)i, trainedCost, true)) { return(CreateResult.FailedToTrainSkill); } } else if (sac == SkillAdvancementClass.Untrained) { player.UntrainSkill((Skill)i, 0); } } // Set Heritage based Melee and Ranged Masteries GetMasteries(player.HeritageGroup, out WeaponType meleeMastery, out WeaponType rangedMastery); player.SetProperty(PropertyInt.MeleeMastery, (int)meleeMastery); player.SetProperty(PropertyInt.RangedMastery, (int)rangedMastery); // Set innate augs SetInnateAugmentations(player); var isDualWieldTrainedOrSpecialized = player.Skills.TryGetValue(Skill.DualWield, out var dualWield) && dualWield.AdvancementClass > SkillAdvancementClass.Untrained; // grant starter items based on skills var starterGearConfig = StarterGearFactory.GetStarterGearConfiguration(); var grantedWeenies = new List <uint>(); foreach (var skillGear in starterGearConfig.Skills) { //var charSkill = player.Skills[(Skill)skillGear.SkillId]; if (!player.Skills.TryGetValue((Skill)skillGear.SkillId, out var charSkill)) { continue; } if (charSkill.AdvancementClass == SkillAdvancementClass.Trained || charSkill.AdvancementClass == SkillAdvancementClass.Specialized) { foreach (var item in skillGear.Gear) { if (grantedWeenies.Contains(item.WeenieId)) { var existingItem = player.Inventory.Values.FirstOrDefault(i => i.WeenieClassId == item.WeenieId); if (existingItem == null || (existingItem.MaxStackSize ?? 1) <= 1) { continue; } existingItem.SetStackSize(existingItem.StackSize + item.StackSize); continue; } var loot = WorldObjectFactory.CreateNewWorldObject(item.WeenieId); if (loot != null) { if (loot.StackSize.HasValue && loot.MaxStackSize.HasValue) { loot.SetStackSize((item.StackSize <= loot.MaxStackSize) ? item.StackSize : loot.MaxStackSize); } } else { player.TryAddToInventory(CreateIOU(item.WeenieId)); } if (loot != null && player.TryAddToInventory(loot)) { grantedWeenies.Add(item.WeenieId); } if (isDualWieldTrainedOrSpecialized && loot != null) { if (loot.WeenieType == WeenieType.MeleeWeapon) { var dualloot = WorldObjectFactory.CreateNewWorldObject(item.WeenieId); if (dualloot != null) { player.TryAddToInventory(dualloot); } else { player.TryAddToInventory(CreateIOU(item.WeenieId)); } } } } var heritageLoot = skillGear.Heritage.FirstOrDefault(i => i.HeritageId == (ushort)characterCreateInfo.Heritage); if (heritageLoot != null) { foreach (var item in heritageLoot.Gear) { if (grantedWeenies.Contains(item.WeenieId)) { var existingItem = player.Inventory.Values.FirstOrDefault(i => i.WeenieClassId == item.WeenieId); if (existingItem == null || (existingItem.MaxStackSize ?? 1) <= 1) { continue; } existingItem.SetStackSize(existingItem.StackSize + item.StackSize); continue; } var loot = WorldObjectFactory.CreateNewWorldObject(item.WeenieId); if (loot != null) { if (loot.StackSize.HasValue && loot.MaxStackSize.HasValue) { loot.SetStackSize((item.StackSize <= loot.MaxStackSize) ? item.StackSize : loot.MaxStackSize); } } else { player.TryAddToInventory(CreateIOU(item.WeenieId)); } if (loot != null && player.TryAddToInventory(loot)) { grantedWeenies.Add(item.WeenieId); } if (isDualWieldTrainedOrSpecialized && loot != null) { if (loot.WeenieType == WeenieType.MeleeWeapon) { var dualloot = WorldObjectFactory.CreateNewWorldObject(item.WeenieId); if (dualloot != null) { player.TryAddToInventory(dualloot); } else { player.TryAddToInventory(CreateIOU(item.WeenieId)); } } } } } foreach (var spell in skillGear.Spells) { if (charSkill.AdvancementClass == SkillAdvancementClass.Trained && spell.SpecializedOnly == false) { player.AddKnownSpell(spell.SpellId); } else if (charSkill.AdvancementClass == SkillAdvancementClass.Specialized) { player.AddKnownSpell(spell.SpellId); } } } } } else { if (player.CharacterTitleId > 0) { player.AddTitle((uint)player.CharacterTitleId.Value, true); } if (player.Biota.PropertiesSpellBook?.Count > 0) { var i = 0u; foreach (var spell in player.Biota.PropertiesSpellBook) { player.HandleActionAddSpellFavorite((uint)spell.Key, i++, 0); } } } player.Name = characterCreateInfo.Name; player.Character.Name = characterCreateInfo.Name; // Index used to determine the starting location var startArea = characterCreateInfo.StartArea; var starterArea = DatManager.PortalDat.CharGen.StarterAreas[(int)startArea]; player.Location = new Position(starterArea.Locations[0].ObjCellID, starterArea.Locations[0].Frame.Origin.X, starterArea.Locations[0].Frame.Origin.Y, starterArea.Locations[0].Frame.Origin.Z, starterArea.Locations[0].Frame.Orientation.X, starterArea.Locations[0].Frame.Orientation.Y, starterArea.Locations[0].Frame.Orientation.Z, starterArea.Locations[0].Frame.Orientation.W); var instantiation = new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); // ultimate fallback. var spellFreeRide = new Database.Models.World.Spell(); switch (starterArea.Name) { case "OlthoiLair": //todo: check this when olthoi play is allowed in ace spellFreeRide = null; // no training area for olthoi, so they start and fall back to same place. instantiation = new Position(player.Location); break; case "Shoushi": spellFreeRide = DatabaseManager.World.GetCachedSpell(3813); // Free Ride to Shoushi break; case "Yaraq": spellFreeRide = DatabaseManager.World.GetCachedSpell(3814); // Free Ride to Yaraq break; case "Sanamar": spellFreeRide = DatabaseManager.World.GetCachedSpell(3535); // Free Ride to Sanamar break; case "Holtburg": default: spellFreeRide = DatabaseManager.World.GetCachedSpell(3815); // Free Ride to Holtburg break; } if (spellFreeRide != null && spellFreeRide.Name != "") { instantiation = new Position(spellFreeRide.PositionObjCellId.Value, spellFreeRide.PositionOriginX.Value, spellFreeRide.PositionOriginY.Value, spellFreeRide.PositionOriginZ.Value, spellFreeRide.PositionAnglesX.Value, spellFreeRide.PositionAnglesY.Value, spellFreeRide.PositionAnglesZ.Value, spellFreeRide.PositionAnglesW.Value); } player.Instantiation = new Position(instantiation); if (!player.IsOlthoiPlayer) { player.Sanctuary = new Position(player.Location); player.SetProperty(PropertyBool.RecallsDisabled, true); if (PropertyManager.GetBool("pk_server").Item) { player.SetProperty(PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.PK); } else if (PropertyManager.GetBool("pkl_server").Item) { player.SetProperty(PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK); } if ((PropertyManager.GetBool("pk_server").Item || PropertyManager.GetBool("pkl_server").Item) && PropertyManager.GetBool("pk_server_safe_training_academy").Item) { player.SetProperty(PropertyFloat.MinimumTimeSincePk, -PropertyManager.GetDouble("pk_new_character_grace_period").Item); player.SetProperty(PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK); } } if (player is Sentinel || player is Admin) { player.Character.IsPlussed = true; player.CloakStatus = CloakStatus.Off; player.ChannelsAllowed = player.ChannelsActive; } CharacterCreateSetDefaultCharacterOptions(player); return(CreateResult.Success); }
/// <summary> /// Creates an enchantment and interacts with the Enchantment registry. /// Used by Life, Creature, Item, and Void magic /// </summary> /// <param name="target"></param> /// <param name="spell"></param> /// <param name="spellStatMod"></param> /// <param name="castByItem"></param> /// <returns></returns> private string CreateEnchantment(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod, bool castByItem) { double duration; if (castByItem) { duration = -1; } else { duration = spell.Duration; } // create enchantment var enchantment = new Enchantment(target, spellStatMod.SpellId, duration, 1, (uint)EnchantmentMask.CreatureSpells); var stackType = target.EnchantmentManager.Add(enchantment, castByItem); var player = this as Player; var playerTarget = target as Player; var creatureTarget = target as Creature; // build message var suffix = ""; switch (stackType) { case StackType.Refresh: suffix = $", refreshing {spell.Name}"; break; case StackType.Surpass: suffix = $", surpassing {target.EnchantmentManager.Surpass.Name}"; break; case StackType.Surpassed: suffix = $", but it is surpassed by {target.EnchantmentManager.Surpass.Name}"; break; } var targetName = this == target ? "yourself" : target.Name; string message; if (castByItem == true) { message = $"An item casts {spell.Name} on you"; } else { message = $"You cast {spell.Name} on {targetName}{suffix}"; } if (target is Player) { if (stackType != StackType.Surpassed) { playerTarget.Session.Network.EnqueueSend(new GameEventMagicUpdateEnchantment(playerTarget.Session, enchantment)); } if (playerTarget != this) { playerTarget.Session.Network.EnqueueSend(new GameMessageSystemChat($"{Name} cast {spell.Name} on you{suffix}", ChatMessageType.Magic)); } } return(message); }
/// <summary> /// Wrapper around CreateEnchantment for Creature Magic /// </summary> /// <param name="target"></param> /// <param name="spell"></param> /// <param name="spellStatMod"></param> /// <param name="castByItem"></param> /// <returns></returns> protected string CreatureMagic(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod, bool castByItem = false) { return(CreateEnchantment(target, spell, spellStatMod, castByItem)); }
/// <summary> /// Creates the Life Magic spell /// </summary> /// <param name="target"></param> /// <param name="spell"></param> /// <param name="spellStatMod"></param> /// <param name="message"></param> /// <param name="castByItem"></param> /// <returns></returns> protected bool LifeMagic(WorldObject target, SpellBase spell, Database.Models.World.Spell spellStatMod, out string message, bool castByItem = false) { string srcVital, destVital, action; string targetMsg = null; Player player = null; Creature creature = null; if (WeenieClassId == 1) { player = (Player)this; } else if (WeenieType == WeenieType.Creature) { creature = (Creature)this; } Creature spellTarget; if (spell.BaseRangeConstant == 0) { spellTarget = (Creature)this; } else { spellTarget = (Creature)target; } int newSpellTargetVital; switch (spell.MetaSpellType) { case SpellType.Boost: int minBoostValue, maxBoostValue; if ((spellStatMod.BoostVariance + spellStatMod.Boost) < spellStatMod.Boost) { minBoostValue = (int)(spellStatMod.BoostVariance + spellStatMod.Boost); maxBoostValue = (int)spellStatMod.Boost; } else { minBoostValue = (int)spellStatMod.Boost; maxBoostValue = (int)(spellStatMod.BoostVariance + spellStatMod.Boost); } int boost = Physics.Common.Random.RollDice(minBoostValue, maxBoostValue); if (boost < 0) { action = "drain"; } else { action = "restore"; } switch (spellStatMod.DamageType) { case 512: // Mana newSpellTargetVital = (int)(spellTarget.Mana.Current + boost); srcVital = "mana"; if (newSpellTargetVital < spellTarget.Mana.MaxValue) { if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Mana, 0); } else { spellTarget.UpdateVital(spellTarget.Mana, (uint)newSpellTargetVital); } } else { spellTarget.UpdateVital(spellTarget.Mana, spellTarget.Mana.MaxValue); } break; case 256: // Stamina newSpellTargetVital = (int)(spellTarget.Stamina.Current + boost); srcVital = "stamina"; if (newSpellTargetVital < spellTarget.Stamina.MaxValue) { if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Stamina, 0); } else { spellTarget.UpdateVital(spellTarget.Stamina, (uint)newSpellTargetVital); } } else { spellTarget.UpdateVital(spellTarget.Stamina, spellTarget.Stamina.MaxValue); } break; default: // Health newSpellTargetVital = (int)(spellTarget.Health.Current + boost); srcVital = "health"; if (newSpellTargetVital < spellTarget.Health.MaxValue) { if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Health, 0); } else { spellTarget.UpdateVital(spellTarget.Health, (uint)newSpellTargetVital); } } else { spellTarget.UpdateVital(spellTarget.Health, spellTarget.Health.MaxValue); } break; } if (this is Player) { if (spell.BaseRangeConstant == 0) { message = $"You {action} {Math.Abs(boost).ToString()} {srcVital}"; } else { message = $"You {action} {Math.Abs(boost).ToString()} points of {srcVital} from {spellTarget.Name}"; } } else { message = null; } if (target is Player && spell.BaseRangeConstant > 0) { targetMsg = $"{Name} casts {spell.Name} and {action}s {Math.Abs(boost)} points of your {srcVital}."; } break; case SpellType.Transfer: // Calculate the change in vitals of the target Creature caster; if (spell.BaseRangeConstant == 0 && spell.BaseRangeMod == 1) { caster = spellTarget; } else { caster = (Creature)this; } uint vitalChange, casterVitalChange; ResistanceType resistanceDrain, resistanceBoost; if (spellStatMod.Source == (int)PropertyAttribute2nd.Mana) { resistanceDrain = ResistanceType.ManaDrain; } else if (spellStatMod.Source == (int)PropertyAttribute2nd.Stamina) { resistanceDrain = ResistanceType.StaminaDrain; } else { resistanceDrain = ResistanceType.HealthDrain; } vitalChange = (uint)((spellTarget.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) * spellStatMod.Proportion) * spellTarget.GetNaturalResistence(resistanceDrain)); if (spellStatMod.TransferCap != 0) { if (vitalChange > spellStatMod.TransferCap) { vitalChange = (uint)spellStatMod.TransferCap; } } if (spellStatMod.Destination == (int)PropertyAttribute2nd.Mana) { resistanceBoost = ResistanceType.ManaDrain; } else if (spellStatMod.Source == (int)PropertyAttribute2nd.Stamina) { resistanceBoost = ResistanceType.StaminaDrain; } else { resistanceBoost = ResistanceType.HealthDrain; } casterVitalChange = (uint)((vitalChange * (1.0f - spellStatMod.LossPercent)) * spellTarget.GetNaturalResistence(resistanceBoost)); vitalChange = (uint)(casterVitalChange / (1.0f - spellStatMod.LossPercent)); newSpellTargetVital = (int)(spellTarget.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) - vitalChange); // Apply the change in vitals to the target switch (spellStatMod.Source) { case (int)PropertyAttribute2nd.Mana: srcVital = "mana"; if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Mana, 0); } else { spellTarget.UpdateVital(spellTarget.Mana, (uint)newSpellTargetVital); } break; case (int)PropertyAttribute2nd.Stamina: srcVital = "stamina"; if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Stamina, 0); } else { spellTarget.UpdateVital(spellTarget.Stamina, (uint)newSpellTargetVital); } break; default: // Health srcVital = "health"; if (newSpellTargetVital <= 0) { spellTarget.UpdateVital(spellTarget.Health, 0); } else { spellTarget.UpdateVital(spellTarget.Health, (uint)newSpellTargetVital); } break; } // Apply the scaled change in vitals to the caster uint newCasterVital; switch (spellStatMod.Destination) { case (int)PropertyAttribute2nd.Mana: destVital = "mana"; newCasterVital = caster.Mana.Current + casterVitalChange; caster.UpdateVital(caster.Mana, newCasterVital); break; case (int)PropertyAttribute2nd.Stamina: destVital = "stamina"; newCasterVital = caster.Stamina.Current + casterVitalChange; caster.UpdateVital(caster.Stamina, newCasterVital); break; default: // Health destVital = "health"; newCasterVital = caster.Mana.Current + casterVitalChange; caster.UpdateVital(caster.Health, newCasterVital); break; } if (WeenieClassId == 1) { if (target.Guid == Guid) { message = $"You drain {vitalChange.ToString()} points of {srcVital} and apply {casterVitalChange.ToString()} points of {destVital} to yourself"; } else { message = $"You drain {vitalChange.ToString()} points of {srcVital} from {spellTarget.Name} and apply {casterVitalChange.ToString()} to yourself"; } } else { message = null; } if (target is Player && target != this) { targetMsg = $"You lose {vitalChange} points of {srcVital} due to {Name} casting {spell.Name} on you"; } break; case SpellType.LifeProjectile: caster = (Creature)this; uint damage; if (spell.Name.Contains("Blight")) { damage = (uint)(caster.GetCurrentCreatureVital(PropertyAttribute2nd.Mana) * caster.GetNaturalResistence(ResistanceType.ManaDrain)); newCasterVital = caster.Mana.Current - damage; if (newCasterVital <= 0) { caster.UpdateVital(caster.Mana, 0); } else { caster.UpdateVital(caster.Mana, (uint)newCasterVital); } } else if (spell.Name.Contains("Tenacity")) { damage = (uint)(spellTarget.GetCurrentCreatureVital(PropertyAttribute2nd.Stamina) * spellTarget.GetNaturalResistence(ResistanceType.StaminaDrain)); newCasterVital = caster.Stamina.Current - damage; if (newCasterVital <= 0) { caster.UpdateVital(caster.Stamina, 0); } else { caster.UpdateVital(caster.Stamina, (uint)newCasterVital); } } else { damage = (uint)(spellTarget.GetCurrentCreatureVital(PropertyAttribute2nd.Stamina) * spellTarget.GetNaturalResistence(ResistanceType.HealthDrain)); newCasterVital = caster.Health.Current - damage; if (newCasterVital <= 0) { caster.UpdateVital(caster.Health, 0); } else { caster.UpdateVital(caster.Health, (uint)newCasterVital); } } CreateSpellProjectile(this, target, spell.MetaSpellId, (uint)spellStatMod.Wcid, damage); if (caster.Health.Current <= 0) { caster.Die(); if (caster.WeenieClassId == 1) { Strings.DeathMessages.TryGetValue(DamageType.Base, out var messages); player.Session.Network.EnqueueSend(new GameMessageSystemChat("You have killed yourself", ChatMessageType.Broadcast)); } } message = null; break; case SpellType.Dispel: message = "Spell not implemented, yet!"; break; case SpellType.Enchantment: message = CreateEnchantment(target, spell, spellStatMod, castByItem); break; default: message = "Spell not implemented, yet!"; break; } if (targetMsg != null) { var playerTarget = target as Player; playerTarget.Session.Network.EnqueueSend(new GameMessageSystemChat(targetMsg, ChatMessageType.Magic)); } if (spellTarget.Health.Current == 0) { return(true); } else { return(false); } }
public static bool TryConvert(uint id, Models.SpellValue input, out Database.Models.World.Spell result) { try { result = new Database.Models.World.Spell(); result.Id = id; result.Name = input.Name; if (input.MetaSpell.Spell.StatMod != null) { result.StatModType = input.MetaSpell.Spell.StatMod.Type; result.StatModKey = input.MetaSpell.Spell.StatMod.Key; result.StatModVal = (float)input.MetaSpell.Spell.StatMod.Val; } // Projectile, LifeProjectile result.EType = input.MetaSpell.Spell.EType; result.BaseIntensity = input.MetaSpell.Spell.BaseIntensity; result.Variance = input.MetaSpell.Spell.Variance; result.Wcid = input.MetaSpell.Spell.Wcid; result.NumProjectiles = input.MetaSpell.Spell.NumProjectiles; result.NumProjectilesVariance = (int?)input.MetaSpell.Spell.NumProjectilesVariance; result.SpreadAngle = input.MetaSpell.Spell.SpreadAngle; result.VerticalAngle = input.MetaSpell.Spell.VerticalAngle; result.DefaultLaunchAngle = input.MetaSpell.Spell.DefaultLaunchAngle; result.NonTracking = input.MetaSpell.Spell.NonTracking; if (input.MetaSpell.Spell.CreateOffset != null) { result.CreateOffsetOriginX = (float)input.MetaSpell.Spell.CreateOffset.X; result.CreateOffsetOriginY = (float)input.MetaSpell.Spell.CreateOffset.Y; result.CreateOffsetOriginZ = (float)input.MetaSpell.Spell.CreateOffset.Z; } if (input.MetaSpell.Spell.Padding != null) { result.PaddingOriginX = (float)input.MetaSpell.Spell.Padding.X; result.PaddingOriginY = (float)input.MetaSpell.Spell.Padding.Y; result.PaddingOriginZ = (float)input.MetaSpell.Spell.Padding.Z; } if (input.MetaSpell.Spell.Dims != null) { result.DimsOriginX = (float)input.MetaSpell.Spell.Dims.X; result.DimsOriginY = (float)input.MetaSpell.Spell.Dims.Y; result.DimsOriginZ = (float)input.MetaSpell.Spell.Dims.Z; } if (input.MetaSpell.Spell.Peturbation != null) { result.PeturbationOriginX = (float)input.MetaSpell.Spell.Peturbation.X; result.PeturbationOriginY = (float)input.MetaSpell.Spell.Peturbation.Y; result.PeturbationOriginZ = (float)input.MetaSpell.Spell.Peturbation.Z; } result.ImbuedEffect = input.MetaSpell.Spell.ImbuedEffect; result.SlayerCreatureType = input.MetaSpell.Spell.SlayerCreatureType; result.SlayerDamageBonus = input.MetaSpell.Spell.SlayerDamageBonus; result.CritFreq = input.MetaSpell.Spell.CritFreq; result.CritMultiplier = input.MetaSpell.Spell.CritMultiplier; result.IgnoreMagicResist = input.MetaSpell.Spell.IgnoreMagicResist; result.ElementalModifier = input.MetaSpell.Spell.ElementalModifier; // LifeProjectile result.DrainPercentage = (float?)input.MetaSpell.Spell.DrainPercentage; result.DamageRatio = (float?)input.MetaSpell.Spell.DamageRatio; // Boost, FellowBoost result.DamageType = input.MetaSpell.Spell.DamageType; result.Boost = input.MetaSpell.Spell.Boost; result.BoostVariance = input.MetaSpell.Spell.BoostVariance; // Transfer result.Source = input.MetaSpell.Spell.Source; result.Destination = input.MetaSpell.Spell.Dest; result.Proportion = (float?)input.MetaSpell.Spell.Proportion; result.LossPercent = (float?)input.MetaSpell.Spell.LossPercent; result.SourceLoss = input.MetaSpell.Spell.SourceLoss; result.TransferCap = input.MetaSpell.Spell.TransferCap; result.MaxBoostAllowed = input.MetaSpell.Spell.MaxBoostAllowed; result.TransferBitfield = input.MetaSpell.Spell.Bitfield; // PortalLink result.Index = input.MetaSpell.Spell.Index; // PortalSummon result.Link = input.MetaSpell.Spell.Link; // PortalSending, FellowPortalSending if (input.MetaSpell.Spell.Position != null) { result.PositionObjCellId = input.MetaSpell.Spell.Position.ObjCellId; result.PositionOriginX = (float)input.MetaSpell.Spell.Position.Frame.Origin.X; result.PositionOriginY = (float)input.MetaSpell.Spell.Position.Frame.Origin.Y; result.PositionOriginZ = (float)input.MetaSpell.Spell.Position.Frame.Origin.Z; result.PositionAnglesW = (float)input.MetaSpell.Spell.Position.Frame.Angles.W; result.PositionAnglesX = (float)input.MetaSpell.Spell.Position.Frame.Angles.X; result.PositionAnglesY = (float)input.MetaSpell.Spell.Position.Frame.Angles.Y; result.PositionAnglesZ = (float)input.MetaSpell.Spell.Position.Frame.Angles.Z; } // Dispel, FellowDispel result.MinPower = input.MetaSpell.Spell.MinPower; result.MaxPower = input.MetaSpell.Spell.MaxPower; result.PowerVariance = input.MetaSpell.Spell.PowerVariance; //result.DispelSchool = input.DispelSchool; // TODO!!! result.Align = input.MetaSpell.Spell.Align; result.Number = input.MetaSpell.Spell.Number; result.NumberVariance = (float?)input.MetaSpell.Spell.NumberVariance; return(true); } catch { result = null; return(false); } }
public Database.Models.World.Spell Surpass; // retval /// <summary> /// Add/update an enchantment in this object's registry /// </summary> public StackType Add(Enchantment enchantment, bool castByItem) { // check for existing spell in this category var entry = GetCategory(enchantment.Spell.Category); /* * if (enchantment.Spell.Power > entry.PowerLevel) * { * // surpass existing spell * Surpass = DatabaseManager.World.GetCachedSpell((uint)entry.SpellId); * Remove(entry, false); * entry = BuildEntry(enchantment.Spell.SpellId); * entry.Duration = -1; * entry.StartTime = 0; * entry.LayerId = enchantment.Layer; * WorldObject.Biota.BiotaPropertiesEnchantmentRegistry.Add(entry); * return StackType.Surpass; * } * * // TODO: the refresh spell case may need some additional functionality to get working correctly * if (enchantment.Spell.Power == entry.PowerLevel) * { * if (entry.Duration != -1) * { * // refresh existing spell with a no countdown timer * entry.LayerId++; * entry.Duration = -1; * entry.StartTime = 0; * return StackType.Refresh; * } * * return StackType.None; * } * * // superior existing spell * Surpass = DatabaseManager.World.GetCachedSpell((uint)entry.SpellId); * return StackType.Surpassed; * } */ // if none, add new record if (entry == null) { entry = BuildEntry(enchantment.Spell.SpellId, castByItem); entry.LayerId = enchantment.Layer; var type = (EnchantmentTypeFlags)entry.StatModType; WorldObject.Biota.BiotaPropertiesEnchantmentRegistry.Add(entry); return(StackType.Initial); } if (enchantment.Spell.Power > entry.PowerLevel) { // surpass existing spell Surpass = DatabaseManager.World.GetCachedSpell((uint)entry.SpellId); Remove(entry, false); entry = BuildEntry(enchantment.Spell.SpellId, castByItem); entry.LayerId = enchantment.Layer; WorldObject.Biota.BiotaPropertiesEnchantmentRegistry.Add(entry); return(StackType.Surpass); } if (enchantment.Spell.Power == entry.PowerLevel) { if (entry.Duration != -1) { // refresh existing spell entry.LayerId++; entry.StartTime = 0; return(StackType.Refresh); } return(StackType.None); } // superior existing spell Surpass = DatabaseManager.World.GetCachedSpell((uint)entry.SpellId); return(StackType.Surpassed); }
/// <summary> /// Method used for handling player untargeted spell casts /// </summary> public void CreatePlayerSpell(uint spellId) { if (IsBusy == true) { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.YoureTooBusy)); return; } else { IsBusy = true; } SpellTable spellTable = DatManager.PortalDat.SpellTable; if (!spellTable.Spells.ContainsKey(spellId)) { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.MagicInvalidSpellType)); IsBusy = false; return; } SpellBase spell = spellTable.Spells[spellId]; Database.Models.World.Spell spellStatMod = DatabaseManager.World.GetCachedSpell(spellId); if (spellStatMod == null) { Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.MagicInvalidSpellType)); IsBusy = false; return; } // Grab player's skill level in the spell's Magic School var magicSkill = GetCreatureSkill(spell.School); float scale = SpellAttributes(Session.Account, spellId, out float castingDelay, out MotionCommand windUpMotion, out MotionCommand spellGesture); var formula = SpellTable.GetSpellFormula(spellTable, spellId, Session.Account); bool spellCastSuccess = false || ((Physics.Common.Random.RollDice(0.0f, 1.0f) > (1.0f - SkillCheck.GetMagicSkillChance((int)magicSkill.Current, (int)spell.Power))) && (magicSkill.Current >= (int)spell.Power - 50) && (magicSkill.Current > 0)); // Calculating mana usage #region if (spellCastSuccess == true) { CreatureSkill mc = GetCreatureSkill(Skill.ManaConversion); double z = mc.Current; double baseManaPercent = 1; if (z > spell.Power) { baseManaPercent = spell.Power / z; } double preCost; uint manaUsed; if ((int)Math.Floor(baseManaPercent) == 1) { preCost = spell.BaseMana; manaUsed = (uint)preCost; } else { preCost = spell.BaseMana * baseManaPercent; if (preCost < 1) { preCost = 1; } manaUsed = (uint)Physics.Common.Random.RollDice(1, (int)preCost); } if (spell.MetaSpellType == SpellType.Transfer) { uint vitalChange, casterVitalChange; vitalChange = (uint)(GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) * spellStatMod.Proportion); if (spellStatMod.TransferCap != 0) { if (vitalChange > spellStatMod.TransferCap) { vitalChange = (uint)spellStatMod.TransferCap; } } casterVitalChange = (uint)(vitalChange * (1.0f - spellStatMod.LossPercent)); vitalChange = (uint)(casterVitalChange / (1.0f - spellStatMod.LossPercent)); if (spellStatMod.Source == (int)PropertyAttribute2nd.Mana && (vitalChange + 10 + manaUsed) > Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } if ((vitalChange + 10) > GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source)) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => IsBusy = false); resourceCheckChain.EnqueueChain(); return; } } else if (manaUsed > Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } else { UpdateVital(Mana, Mana.Current - manaUsed); } } #endregion ActionChain spellChain = new ActionChain(); uint fastCast = (spell.Bitfield >> 14) & 0x1u; if (fastCast != 1) { spellChain.AddAction(this, () => { var motionWindUp = new UniversalMotion(MotionStance.Spellcasting); motionWindUp.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionWindUp.MovementData.ForwardCommand = (uint)windUpMotion; motionWindUp.MovementData.ForwardSpeed = 2; DoMotion(motionWindUp); }); } spellChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageCreatureMessage(SpellComponentsTable.GetSpellWords(DatManager.PortalDat.SpellComponentsTable, formula), Name, Guid.Full, ChatMessageType.Magic)); }); spellChain.AddAction(this, () => { var motionCastSpell = new UniversalMotion(MotionStance.Spellcasting); motionCastSpell.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionCastSpell.MovementData.ForwardCommand = (uint)spellGesture; motionCastSpell.MovementData.ForwardSpeed = 2; DoMotion(motionCastSpell); }); if (fastCast == 1) { spellChain.AddDelaySeconds(castingDelay * 0.26f); } else { spellChain.AddDelaySeconds(castingDelay); } if (spellCastSuccess == false) { spellChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f))); } else { // TODO - Successful spell casting code goes here for untargeted spells to replace line below Session.Network.EnqueueSend(new GameMessageSystemChat("Targeted SpellID " + spellId + " not yet implemented!", ChatMessageType.System)); } spellChain.AddAction(this, () => { var motionReturnToCastStance = new UniversalMotion(MotionStance.Spellcasting); motionReturnToCastStance.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionReturnToCastStance.MovementData.ForwardCommand = (uint)MotionCommand.Invalid; DoMotion(motionReturnToCastStance); }); spellChain.AddDelaySeconds(1.0f); spellChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameEventUseDone(Session, errorType: WeenieError.None)); IsBusy = false; }); spellChain.EnqueueChain(); return; }
/// <summary> /// Method used for handling player targeted spell casts /// </summary> public void CreatePlayerSpell(ObjectGuid guidTarget, uint spellId) { Player player = CurrentLandblock.GetObject(Guid) as Player; WorldObject target = CurrentLandblock.GetObject(guidTarget); TargetCategory targetCategory = TargetCategory.WorldObject; if (target == null) { target = GetWieldedItem(guidTarget); targetCategory = TargetCategory.Wielded; } if (target == null) { target = GetInventoryItem(guidTarget); targetCategory = TargetCategory.Inventory; } if (target == null) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.TargetNotAcquired)); targetCategory = TargetCategory.UnDef; return; } SpellTable spellTable = DatManager.PortalDat.SpellTable; if (!spellTable.Spells.ContainsKey(spellId)) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicInvalidSpellType)); return; } SpellBase spell = spellTable.Spells[spellId]; if (IsInvalidTarget(spell, target)) { player.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(player.Session, $"{spell.Name} cannot be cast on {target.Name}.")); player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.None)); return; } Database.Models.World.Spell spellStatMod = DatabaseManager.World.GetCachedSpell(spellId); if (spellStatMod == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicInvalidSpellType)); return; } if (player.IsBusy == true) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YoureTooBusy)); return; } else { player.IsBusy = true; } uint targetEffect = spell.TargetEffect; // Grab player's skill level in the spell's Magic School var magicSkill = player.GetCreatureSkill(spell.School).Current; if (targetCategory == TargetCategory.WorldObject) { if (guidTarget != Guid) { float distanceTo = Location.Distance2D(target.Location); if (distanceTo > spell.BaseRangeConstant + magicSkill * spell.BaseRangeMod) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicTargetOutOfRange), new GameMessageSystemChat($"{target.Name} is out of range!", ChatMessageType.Magic)); player.IsBusy = false; return; } } } // Ensure that a harmful spell isn't being cast on a player target that doesn't have the same PK status if (target.WeenieClassId == 1 && player.PlayerKillerStatus != ACE.Entity.Enum.PlayerKillerStatus.NPK) { bool isSpellHarmful = IsSpellHarmful(spell); if (player.PlayerKillerStatus != target.PlayerKillerStatus && isSpellHarmful) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.InvalidPkStatus)); player.IsBusy = false; return; } } float scale = SpellAttributes(player.Session.Account, spellId, out float castingDelay, out MotionCommand windUpMotion, out MotionCommand spellGesture); var formula = SpellTable.GetSpellFormula(spellTable, spellId, player.Session.Account); bool spellCastSuccess = false || ((Physics.Common.Random.RollDice(0.0f, 1.0f) > (1.0f - SkillCheck.GetMagicSkillChance((int)magicSkill, (int)spell.Power))) && (magicSkill >= (int)spell.Power - 50) && (magicSkill > 0)); // Calculating mana usage #region CreatureSkill mc = player.GetCreatureSkill(Skill.ManaConversion); double z = mc.Current; double baseManaPercent = 1; if (z > spell.Power) { baseManaPercent = spell.Power / z; } double preCost; uint manaUsed; if ((int)Math.Floor(baseManaPercent) == 1) { preCost = spell.BaseMana; manaUsed = (uint)preCost; } else { preCost = spell.BaseMana * baseManaPercent; if (preCost < 1) { preCost = 1; } manaUsed = (uint)Physics.Common.Random.RollDice(1, (int)preCost); } if (spell.MetaSpellType == SpellType.Transfer) { uint vitalChange, casterVitalChange; vitalChange = (uint)(player.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source) * spellStatMod.Proportion); if (spellStatMod.TransferCap != 0) { if (vitalChange > spellStatMod.TransferCap) { vitalChange = (uint)spellStatMod.TransferCap; } } casterVitalChange = (uint)(vitalChange * (1.0f - spellStatMod.LossPercent)); vitalChange = (uint)(casterVitalChange / (1.0f - spellStatMod.LossPercent)); if (spellStatMod.Source == (int)PropertyAttribute2nd.Mana && (vitalChange + 10 + manaUsed) > player.Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); player.IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } else if ((vitalChange + 10) > player.GetCurrentCreatureVital((PropertyAttribute2nd)spellStatMod.Source)) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => player.IsBusy = false); resourceCheckChain.EnqueueChain(); return; } } else if (manaUsed > player.Mana.Current) { ActionChain resourceCheckChain = new ActionChain(); resourceCheckChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f)); }); resourceCheckChain.AddDelaySeconds(2.0f); resourceCheckChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.YouDontHaveEnoughManaToCast)); player.IsBusy = false; }); resourceCheckChain.EnqueueChain(); return; } else { player.UpdateVital(player.Mana, player.Mana.Current - manaUsed); } #endregion ActionChain spellChain = new ActionChain(); uint fastCast = (spell.Bitfield >> 14) & 0x1u; if (fastCast != 1) { spellChain.AddAction(this, () => { var motionWindUp = new UniversalMotion(MotionStance.Spellcasting); motionWindUp.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionWindUp.MovementData.ForwardCommand = (uint)windUpMotion; motionWindUp.MovementData.ForwardSpeed = 2; DoMotion(motionWindUp); }); } spellChain.AddAction(this, () => { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageCreatureMessage(SpellComponentsTable.GetSpellWords(DatManager.PortalDat.SpellComponentsTable, formula), Name, Guid.Full, ChatMessageType.Magic)); }); spellChain.AddAction(this, () => { var motionCastSpell = new UniversalMotion(MotionStance.Spellcasting); motionCastSpell.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionCastSpell.MovementData.ForwardCommand = (uint)spellGesture; motionCastSpell.MovementData.ForwardSpeed = 2; DoMotion(motionCastSpell); }); if (fastCast == 1) { spellChain.AddDelaySeconds(castingDelay * 0.26f); } else { spellChain.AddDelaySeconds(castingDelay); } if (spellCastSuccess == false) { spellChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, ACE.Entity.Enum.PlayScript.Fizzle, 0.5f))); } else { spellChain.AddAction(this, () => { bool targetDeath; string message; switch (spell.School) { case MagicSchool.WarMagic: WarMagic(target, spell, spellStatMod); break; case MagicSchool.VoidMagic: VoidMagic(target, spell, spellStatMod); break; case MagicSchool.CreatureEnchantment: if (IsSpellHarmful(spell)) { // Retrieve player's skill level in the Magic School var playerMagicSkill = player.GetCreatureSkill(spell.School).Current; // Retrieve target's Magic Defense Skill Creature creature = (Creature)target; var targetMagicDefenseSkill = creature.GetCreatureSkill(Skill.MagicDefense).Current; if (MagicDefenseCheck(playerMagicSkill, targetMagicDefenseSkill)) { CurrentLandblock.EnqueueBroadcastSound(player, Sound.ResistSpell); player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{creature.Name} resists {spell.Name}", ChatMessageType.Magic)); break; } } CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); message = CreatureMagic(target, spell, spellStatMod); if (message != "") { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; case MagicSchool.LifeMagic: if (spell.MetaSpellType != SpellType.LifeProjectile) { if (IsSpellHarmful(spell)) { // Retrieve player's skill level in the Magic School var playerMagicSkill = player.GetCreatureSkill(spell.School).Current; // Retrieve target's Magic Defense Skill Creature creature = (Creature)target; var targetMagicDefenseSkill = creature.GetCreatureSkill(Skill.MagicDefense).Current; if (MagicDefenseCheck(playerMagicSkill, targetMagicDefenseSkill)) { CurrentLandblock.EnqueueBroadcastSound(player, Sound.ResistSpell); player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{creature.Name} resists {spell.Name}", ChatMessageType.Magic)); break; } } } CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); targetDeath = LifeMagic(target, spell, spellStatMod, out message); if (message != null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } if (targetDeath == true) { Creature creatureTarget = (Creature)target; creatureTarget.Die(); Strings.DeathMessages.TryGetValue(DamageType.Base, out var messages); player.Session.Network.EnqueueSend(new GameMessageSystemChat(string.Format(messages[0], target.Name), ChatMessageType.Broadcast)); player.EarnXP((long)target.XpOverride); } break; case MagicSchool.ItemEnchantment: if (guidTarget == Guid) { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(Guid, (PlayScript)spell.CasterEffect, scale)); } else { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(target.Guid, (PlayScript)spell.TargetEffect, scale)); } message = ItemMagic(target, spell, spellStatMod); if (message != "") { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; default: break; } }); } spellChain.AddAction(this, () => { var motionReturnToCastStance = new UniversalMotion(MotionStance.Spellcasting); motionReturnToCastStance.MovementData.CurrentStyle = (ushort)((uint)MotionStance.Spellcasting & 0xFFFF); motionReturnToCastStance.MovementData.ForwardCommand = (uint)MotionCommand.Invalid; DoMotion(motionReturnToCastStance); }); spellChain.AddDelaySeconds(1.0f); spellChain.AddAction(this, () => { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.None)); player.IsBusy = false; }); spellChain.EnqueueChain(); return; }
/// <summary> /// Method used for handling items casting spells on the player equiping the item /// </summary> /// <param name="guidItem"></param> /// <param name="spellId"></param> public void CreateItemSpell(ObjectGuid guidItem, uint spellId) { Player player = CurrentLandblock.GetObject(Guid) as Player; WorldObject item = player.GetWieldedItem(guidItem); if (item == null) { item = player.GetInventoryItem(guidItem); if (item == null) { return; } if (item.WeenieType != WeenieType.Gem) { return; } } CreatureSkill arcaneLore = player.GetCreatureSkill(Skill.ArcaneLore); CreatureSkill meleeDefense = player.GetCreatureSkill(Skill.MeleeDefense); CreatureSkill missileDefense = player.GetCreatureSkill(Skill.MissileDefense); CreatureSkill magicDefense = player.GetCreatureSkill(Skill.MagicDefense); if (arcaneLore.Current >= item.ItemDifficulty || item.ItemDifficulty == null) { if (item.AppraisalItemSkill != 0 || item.AppraisalItemSkill != null) { switch (item.AppraisalItemSkill) { case 6: if (meleeDefense.Current < item.ItemSkillLevelLimit) { return; } break; case 7: if (missileDefense.Current < item.ItemSkillLevelLimit) { return; } break; case 8: if (magicDefense.Current < item.ItemSkillLevelLimit) { return; } break; } } SpellTable spellTable = DatManager.PortalDat.SpellTable; if (!spellTable.Spells.ContainsKey(spellId)) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, errorType: WeenieError.MagicInvalidSpellType)); return; } SpellBase spell = spellTable.Spells[spellId]; Database.Models.World.Spell spellStatMod = DatabaseManager.World.GetCachedSpell(spellId); if (spellStatMod == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{spell.Name} spell not implemented, yet!", ChatMessageType.System)); return; } float scale = SpellAttributes(player.Session.Account, spellId, out float castingDelay, out MotionCommand windUpMotion, out MotionCommand spellGesture); string message; switch (spell.School) { case MagicSchool.CreatureEnchantment: if (IsSpellHarmful(spell)) { break; } CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(player.Guid, (PlayScript)spell.TargetEffect, scale)); message = CreatureMagic(player, spell, spellStatMod, item.Name); if (message != "") { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; case MagicSchool.LifeMagic: if (spell.MetaSpellType != SpellType.LifeProjectile) { if (IsSpellHarmful(spell)) { break; } } CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(player.Guid, (PlayScript)spell.TargetEffect, scale)); LifeMagic(player, spell, spellStatMod, out message, item.Name); if (message != null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; case MagicSchool.ItemEnchantment: CurrentLandblock.EnqueueBroadcast(Location, new GameMessageScript(item.Guid, (PlayScript)spell.TargetEffect, scale)); message = ItemMagic(item, spell, spellStatMod, item.Name); if (message != "") { player.Session.Network.EnqueueSend(new GameMessageSystemChat(message, ChatMessageType.Magic)); } break; default: break; } } }
/// <summary> /// Add/update an enchantment in this object's registry /// </summary> public StackType Add(Enchantment enchantment, WorldObject caster) { StackType result = StackType.Undef; // check for existing spell in this category var entries = GetCategory(enchantment.SpellBase.Category); // if none, add new record if (entries.Count == 0) { var newEntry = BuildEntry(enchantment.Spell.Id, caster); newEntry.LayerId = enchantment.Layer; WorldObject.Biota.AddEnchantment(newEntry, WorldObject.BiotaDatabaseLock); WorldObject.ChangesDetected = true; return(StackType.Initial); } // Check for existing spells in registry that are superior foreach (var entry in entries) { if (enchantment.SpellBase.Power < entry.PowerLevel) { // superior existing spell Surpass = DatabaseManager.World.GetCachedSpell((uint)entry.SpellId); result = StackType.Surpassed; } } if (result != StackType.Surpassed) { // Check for existing spells in registry that are equal to foreach (var entry in entries) { if (enchantment.SpellBase.Power == entry.PowerLevel) { if (entry.Duration == -1) { result = StackType.None; break; } // item cast spell of equal power should override an existing spell, especially one with a duration if ((caster as Creature) == null) { enchantment.Layer = entry.LayerId; // Should be a higher layer than existing enchant var newEntry = BuildEntry(enchantment.Spell.Id, caster); newEntry.LayerId = enchantment.Layer; WorldObject.Biota.AddEnchantment(newEntry, WorldObject.BiotaDatabaseLock); WorldObject.ChangesDetected = true; result = StackType.Refresh; break; } // refresh existing spell entry.StartTime = 0; result = StackType.Refresh; break; } } // Previous check didn't return any result if (result == StackType.Undef) { ushort layerBuffer = 1; // Check for highest existing spell in registry that is inferior foreach (var entry in entries) { if (enchantment.SpellBase.Power > entry.PowerLevel) { // surpass existing spell Surpass = DatabaseManager.World.GetCachedSpell((uint)entry.SpellId); layerBuffer = entry.LayerId; } } enchantment.Layer = (ushort)(layerBuffer + 1); // Should be a higher layer than existing enchant var newEntry = BuildEntry(enchantment.Spell.Id, caster); newEntry.LayerId = enchantment.Layer; WorldObject.Biota.AddEnchantment(newEntry, WorldObject.BiotaDatabaseLock); WorldObject.ChangesDetected = true; result = StackType.Surpass; } } return(result); }