public void HandleActionQueryItemMana(ObjectGuid queryId) { if (queryId.Full == 0) { ManaQueryTarget = null; return; } // the object could be in the world or on the player, first check player WorldObject wo = GetInventoryItem(queryId); if (wo != null) { wo.QueryItemMana(Session); } else { // We could be wielded - let's check that next. if (EquippedObjects.TryGetValue(queryId, out wo)) { wo.QueryItemMana(Session); } } ManaQueryTarget = queryId.Full; }
/// <summary> /// This method sets us into peace mode. It checks our current state to see if we have missle ammo equipped /// it will make the call to hid the "ammo" as we switch to peace mode. It will then send the message switch our stance. Og II /// </summary> /// <param name="oldCombatMode"></param> /// <param name="isAutonomous"></param> public void HandleSwitchToPeaceMode(CombatMode oldCombatMode, bool isAutonomous = false) { HandleUnloadMissileAmmo(oldCombatMode); // FIXME: (Og II)<this is a hack for now to be removed.> Placement has an issue we have not figured out. It has to do with animation frame. Og II PositionFlag &= ~UpdatePositionFlag.Placement; // End hack CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this)); UniversalMotion mm = new UniversalMotion(MotionStance.Standing); mm.MovementData.CurrentStyle = (uint)MotionStance.Standing; SetMotionState(this, mm); var mEquipedAmmo = EquippedObjects.FirstOrDefault(s => s.Value.CurrentWieldedLocation == EquipMask.MissileAmmo).Value; var player = this as Player; if (mEquipedAmmo != null) { CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectGhostRange, new GameMessagePickupEvent(mEquipedAmmo)); } if (player != null) { player.stance = MotionStance.Standing; player.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.NonCombat)); } }
public void AuditItemSpells() { // cleans up bugged chars with dangling item set spells // from previous bugs // this is a legacy method, but is still a decent failsafe to catch any existing issues // get active item enchantments var enchantments = Biota.GetEnchantments(BiotaDatabaseLock).Where(i => i.Duration == -1 && i.SpellId != (int)SpellId.Vitae).ToList(); foreach (var enchantment in enchantments) { // if this item is not equipped, remove enchantment if (!EquippedObjects.TryGetValue(new ObjectGuid(enchantment.CasterObjectId), out var item)) { // this can fail for item sets, so disabling this section /*var spell = new Spell(enchantment.SpellId, false); * log.Error($"{Name}.AuditItemSpells(): removing spell {spell.Name} from non-equipped item"); * * EnchantmentManager.Dispel(enchantment);*/ continue; } // is this item part of a set? if (!item.HasItemSet) { continue; } // get all of the equipped items in this set var setItems = EquippedObjects.Values.Where(i => i.HasItemSet && i.EquipmentSetId == item.EquipmentSetId).ToList(); // get all of the spells currently active from this set var currentSpells = GetSpellSet(setItems); // get all of the spells possible for this item set var possibleSpells = GetSpellSetAll((EquipmentSet)item.EquipmentSetId); // get the difference between them var inactiveSpells = possibleSpells.Except(currentSpells).ToList(); // remove any item set spells that shouldn't be active foreach (var inactiveSpell in inactiveSpells) { var removeSpells = enchantments.Where(i => i.SpellSetId == (uint)item.EquipmentSetId && i.SpellId == inactiveSpell.Id).ToList(); foreach (var removeSpell in removeSpells) { log.Error($"{Name}.AuditItemSpells(): removing spell {inactiveSpell.Name} from {item.EquipmentSetId}"); EnchantmentManager.Dispel(removeSpell); } } } }
/// <summary> /// Called every ~5 secs for equipped mana consuming items /// </summary> public void ManaConsumersTick() { if (!EquippedObjectsLoaded) { return; } var EquippedManaConsumers = EquippedObjects.Where(k => (k.Value.IsAffecting ?? false) && //k.Value.ManaRate.HasValue && k.Value.ItemMaxMana.HasValue && k.Value.ItemCurMana.HasValue && k.Value.ItemCurMana.Value > 0).ToList(); foreach (var k in EquippedManaConsumers) { var item = k.Value; // this was a bug in lootgen until 7/11/2019, mostly for clothing/armor/shields // tons of existing items on servers are in this bugged state, where they aren't ticking mana. // this retroactively fixes them when equipped // items such as Impious Staff are excluded from this via IsAffecting if (item.ManaRate == null) { item.ManaRate = LootGenerationFactory.GetManaRate(item); log.Warn($"{Name}.ManaConsumersTick(): {k.Value.Name} ({k.Value.Guid}) fixed missing ManaRate"); } var rate = item.ManaRate.Value; if (LumAugItemManaUsage != 0) { rate *= GetNegativeRatingMod(LumAugItemManaUsage * 5); } if (!item.ItemManaConsumptionTimestamp.HasValue) { item.ItemManaConsumptionTimestamp = DateTime.UtcNow; } DateTime mostRecentBurn = item.ItemManaConsumptionTimestamp.Value; var timePerBurn = -1 / rate; var secondsSinceLastBurn = (DateTime.UtcNow - mostRecentBurn).TotalSeconds; var delta = secondsSinceLastBurn / timePerBurn; var deltaChopped = (int)Math.Floor(delta); var deltaExtra = delta - deltaChopped; if (deltaChopped <= 0) { continue; } var timeToAdd = (int)Math.Floor(deltaChopped * timePerBurn); item.ItemManaConsumptionTimestamp = mostRecentBurn + new TimeSpan(0, 0, timeToAdd); var manaToBurn = Math.Min(item.ItemCurMana.Value, deltaChopped); deltaChopped = Math.Clamp(deltaChopped, 0, 10); item.ItemCurMana -= deltaChopped; if (item.ItemCurMana < 1 || item.ItemCurMana == null) { item.IsAffecting = false; var msg = new GameMessageSystemChat($"Your {item.Name} is out of Mana.", ChatMessageType.Magic); var sound = new GameMessageSound(Guid, Sound.ItemManaDepleted); Session.Network.EnqueueSend(msg, sound); if (item.WielderId != null) { if (item.Biota.PropertiesSpellBook != null) { // unsure if these messages / sounds were ever sent in retail, // or if it just purged the enchantments invisibly // doing a delay here to prevent 'SpellExpired' sounds from overlapping with 'ItemManaDepleted' var actionChain = new ActionChain(); actionChain.AddDelaySeconds(2.0f); actionChain.AddAction(this, () => { foreach (var spellId in item.Biota.GetKnownSpellsIds(item.BiotaDatabaseLock)) { RemoveItemSpell(item, (uint)spellId); } }); actionChain.EnqueueChain(); } } } else { // get time until empty var secondsUntilEmpty = ((item.ItemCurMana - deltaExtra) * timePerBurn); if (secondsUntilEmpty <= 120 && (!item.ItemManaDepletionMessageTimestamp.HasValue || (DateTime.UtcNow - item.ItemManaDepletionMessageTimestamp.Value).TotalSeconds > 120)) { item.ItemManaDepletionMessageTimestamp = DateTime.UtcNow; Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is low on Mana.", ChatMessageType.Magic)); } } } }
/// <summary> /// Called every ~5 secs for equipped mana consuming items /// </summary> public void ManaConsumersTick() { if (EquippedObjectsLoaded) { var EquippedManaConsumers = EquippedObjects.Where(k => (k.Value.IsAffecting ?? false) && k.Value.ManaRate.HasValue && k.Value.ItemMaxMana.HasValue && k.Value.ItemCurMana.HasValue && k.Value.ItemCurMana.Value > 0).ToList(); EquippedManaConsumers.ForEach(k => { var item = k.Value; var rate = item.ManaRate.Value; if (!item.ItemManaConsumptionTimestamp.HasValue) { item.ItemManaConsumptionTimestamp = DateTime.Now; } DateTime mostRecentBurn = item.ItemManaConsumptionTimestamp.Value; var timePerBurn = -1 / rate; var secondsSinceLastBurn = (DateTime.Now - mostRecentBurn).TotalSeconds; var delta = secondsSinceLastBurn / timePerBurn; var deltaChopped = (int)Math.Floor(delta); var deltaExtra = delta - deltaChopped; if (deltaChopped > 0) { var timeToAdd = (int)Math.Floor(deltaChopped * timePerBurn); item.ItemManaConsumptionTimestamp = mostRecentBurn + new TimeSpan(0, 0, timeToAdd); var manaToBurn = Math.Min(item.ItemCurMana.Value, deltaChopped); deltaChopped = Math.Clamp(deltaChopped, 0, 10); item.ItemCurMana -= deltaChopped; if (item.ItemCurMana < 1 || item.ItemCurMana == null) { item.IsAffecting = false; Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is out of mana.", ChatMessageType.Magic)); if (item.WielderId != null) { if (item.Biota.BiotaPropertiesSpellBook != null) { for (int i = 0; i < item.Biota.BiotaPropertiesSpellBook.Count; i++) { // TODO: layering RemoveItemSpell(item.Guid, (uint)item.Biota.BiotaPropertiesSpellBook.ElementAt(i).Spell); } } } } else { // get time until empty var secondsUntilEmpty = ((item.ItemCurMana - deltaExtra) * timePerBurn); if (secondsUntilEmpty <= 30 && (!item.ItemManaDepletionMessageTimestamp.HasValue || (DateTime.Now - item.ItemManaDepletionMessageTimestamp.Value).TotalSeconds > 30)) { item.ItemManaDepletionMessageTimestamp = DateTime.Now; Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is almost out of mana.", ChatMessageType.Magic)); } } } }); } }
/// <summary> /// Called every ~5 secs for equipped mana consuming items /// </summary> public void ManaConsumersTick() { if (!EquippedObjectsLoaded) { return; } var EquippedManaConsumers = EquippedObjects.Where(k => (k.Value.IsAffecting ?? false) && k.Value.ManaRate.HasValue && k.Value.ItemMaxMana.HasValue && k.Value.ItemCurMana.HasValue && k.Value.ItemCurMana.Value > 0).ToList(); foreach (var k in EquippedManaConsumers) { var item = k.Value; var rate = item.ManaRate.Value; if (LumAugItemManaUsage != 0) { rate *= GetNegativeRatingMod(LumAugItemManaUsage); } if (!item.ItemManaConsumptionTimestamp.HasValue) { item.ItemManaConsumptionTimestamp = DateTime.Now; } DateTime mostRecentBurn = item.ItemManaConsumptionTimestamp.Value; var timePerBurn = -1 / rate; var secondsSinceLastBurn = (DateTime.Now - mostRecentBurn).TotalSeconds; var delta = secondsSinceLastBurn / timePerBurn; var deltaChopped = (int)Math.Floor(delta); var deltaExtra = delta - deltaChopped; if (deltaChopped <= 0) { continue; } var timeToAdd = (int)Math.Floor(deltaChopped * timePerBurn); item.ItemManaConsumptionTimestamp = mostRecentBurn + new TimeSpan(0, 0, timeToAdd); var manaToBurn = Math.Min(item.ItemCurMana.Value, deltaChopped); deltaChopped = Math.Clamp(deltaChopped, 0, 10); item.ItemCurMana -= deltaChopped; if (item.ItemCurMana < 1 || item.ItemCurMana == null) { item.IsAffecting = false; var msg = new GameMessageSystemChat($"Your {item.Name} is out of Mana.", ChatMessageType.Magic); var sound = new GameMessageSound(Guid, Sound.ItemManaDepleted); Session.Network.EnqueueSend(msg, sound); if (item.WielderId != null) { if (item.Biota.BiotaPropertiesSpellBook != null) { // unsure if these messages / sounds were ever sent in retail, // or if it just purged the enchantments invisibly // doing a delay here to prevent 'SpellExpired' sounds from overlapping with 'ItemManaDepleted' var actionChain = new ActionChain(); actionChain.AddDelaySeconds(2.0f); actionChain.AddAction(this, () => { for (int i = 0; i < item.Biota.BiotaPropertiesSpellBook.Count; i++) { RemoveItemSpell(item, (uint)item.Biota.BiotaPropertiesSpellBook.ElementAt(i).Spell); } }); actionChain.EnqueueChain(); } } } else { // get time until empty var secondsUntilEmpty = ((item.ItemCurMana - deltaExtra) * timePerBurn); if (secondsUntilEmpty <= 120 && (!item.ItemManaDepletionMessageTimestamp.HasValue || (DateTime.Now - item.ItemManaDepletionMessageTimestamp.Value).TotalSeconds > 120)) { item.ItemManaDepletionMessageTimestamp = DateTime.Now; Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is low on Mana.", ChatMessageType.Magic)); } } } }
public override ACE.Entity.ObjDesc CalculateObjDesc() { ACE.Entity.ObjDesc objDesc = new ACE.Entity.ObjDesc(); ClothingTable item; AddBaseModelData(objDesc); var coverage = new List <uint>(); foreach (var w in EquippedObjects.OrderBy(x => x.Value.Priority)) { // We can wield things that are not part of our model, only use those items that can cover our model. if ((w.Value.CurrentWieldedLocation & (EquipMask.Clothing | EquipMask.Armor | EquipMask.Cloak)) != 0) { if (w.Value.ClothingBase.HasValue) { item = DatManager.PortalDat.ReadFromDat <ClothingTable>((uint)w.Value.ClothingBase); } else { continue; } if (item.ClothingBaseEffects.ContainsKey(SetupTableId)) // Check if the player model has data. Gear Knights, this is usually you. { // Add the model and texture(s) ClothingBaseEffect clothingBaseEffec = item.ClothingBaseEffects[SetupTableId]; foreach (CloObjectEffect t in clothingBaseEffec.CloObjectEffects) { byte partNum = (byte)t.Index; objDesc.AnimPartChanges.Add(new ACE.Entity.AnimationPartChange { PartIndex = (byte)t.Index, PartID = t.ModelId }); //AddModel((byte)t.Index, (ushort)t.ModelId); coverage.Add(partNum); foreach (CloTextureEffect t1 in t.CloTextureEffects) { objDesc.TextureChanges.Add(new ACE.Entity.TextureMapChange { PartIndex = (byte)t.Index, OldTexture = t1.OldTexture, NewTexture = t1.NewTexture }); } //AddTexture((byte)t.Index, (ushort)t1.OldTexture, (ushort)t1.NewTexture); } if (item.ClothingSubPalEffects.Count > 0) { int size = item.ClothingSubPalEffects.Count; int palCount = size; CloSubPalEffect itemSubPal; int palOption = 0; if (w.Value.PaletteTemplate.HasValue) { palOption = (int)w.Value.PaletteTemplate; } if (item.ClothingSubPalEffects.ContainsKey((uint)palOption)) { itemSubPal = item.ClothingSubPalEffects[(uint)palOption]; } else { itemSubPal = item.ClothingSubPalEffects[item.ClothingSubPalEffects.Keys.ElementAt(0)]; } //if (itemSubPal.Icon > 0) // IconId = itemSubPal.Icon; float shade = 0; if (w.Value.Shade.HasValue) { shade = (float)w.Value.Shade; } for (int i = 0; i < itemSubPal.CloSubPalettes.Count; i++) { var itemPalSet = DatManager.PortalDat.ReadFromDat <PaletteSet>(itemSubPal.CloSubPalettes[i].PaletteSet); ushort itemPal = (ushort)itemPalSet.GetPaletteID(shade); for (int j = 0; j < itemSubPal.CloSubPalettes[i].Ranges.Count; j++) { uint palOffset = itemSubPal.CloSubPalettes[i].Ranges[j].Offset / 8; uint numColors = itemSubPal.CloSubPalettes[i].Ranges[j].NumColors / 8; objDesc.SubPalettes.Add(new ACE.Entity.SubPalette { SubID = itemPal, Offset = palOffset, NumColors = numColors }); //AddPalette(itemPal, (ushort)palOffset, (ushort)numColors); } } } } } } // Add the "naked" body parts. These are the ones not already covered. if (SetupTableId > 0) { var baseSetup = DatManager.PortalDat.ReadFromDat <SetupModel>(SetupTableId); for (byte i = 0; i < baseSetup.Parts.Count; i++) { if (!coverage.Contains(i) && i != 0x10) // Don't add body parts for those that are already covered. Also don't add the head, that was already covered by AddCharacterBaseModelData() { objDesc.AnimPartChanges.Add(new ACE.Entity.AnimationPartChange { PartIndex = i, PartID = baseSetup.Parts[i] }); } //AddModel(i, baseSetup.Parts[i]); } } if (coverage.Count == 0 && ClothingBase.HasValue) { return(base.CalculateObjDesc()); } return(objDesc); }
public void HandleSwitchToMissileCombatMode(ActionChain combatModeChain) { // TODO and FIXME: GetInventoryItem doesn't work for this so this function is effectively broke HeldItem mEquipedMissile = Children.Find(s => s.EquipMask == EquipMask.MissileWeapon); if (mEquipedMissile?.Guid != null) { WorldObject missileWeapon = GetInventoryItem(new ObjectGuid(mEquipedMissile.Guid)); if (missileWeapon == null) { log.InfoFormat("Changing combat mode for {0} - could not locate wielded weapon {1}", Guid, mEquipedMissile.Guid); return; } var mEquipedAmmo = EquippedObjects.First(s => s.Value.CurrentWieldedLocation == EquipMask.MissileAmmo).Value; MotionStance ms; CombatStyle cs; if (missileWeapon.DefaultCombatStyle != null) { cs = missileWeapon.DefaultCombatStyle.Value; } else { log.InfoFormat("Changing combat mode for {0} - wielded item {1} has not be assigned a default combat style", Guid, mEquipedMissile.Guid); return; } switch (cs) { case CombatStyle.Bow: ms = MotionStance.BowAttack; break; case CombatStyle.Crossbow: ms = MotionStance.CrossBowAttack; break; default: ms = MotionStance.Invalid; break; } UniversalMotion mm = new UniversalMotion(ms); mm.MovementData.CurrentStyle = (ushort)ms; if (mEquipedAmmo == null) { CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this)); SetMotionState(this, mm); } else { CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this)); SetMotionState(this, mm); mm.MovementData.ForwardCommand = (uint)MotionCommand.Reload; SetMotionState(this, mm); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this)); // FIXME: (Og II)<this is a hack for now to be removed. Need to pull delay from dat file combatModeChain.AddDelaySeconds(0.25); // System.Threading.Thread.Sleep(250); // used for debugging mm.MovementData.ForwardCommand = (ushort)MotionCommand.Invalid; SetMotionState(this, mm); // FIXME: (Og II)<this is a hack for now to be removed. Need to pull delay from dat file combatModeChain.AddDelaySeconds(0.40); combatModeChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageParentEvent(this, mEquipedAmmo, 1, 1))); // CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageParentEvent(this, ammo, 1, 1)); // used for debugging } CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePrivateUpdatePropertyInt(Sequences, PropertyInt.CombatMode, (int)CombatMode.Missile)); } }