/// <summary> /// This signature services MoveToObject and TurnToObject /// Update Position prior to start, start them moving or turning, set statemachine to moving. /// Moved from player - we need to be able to move creatures as well. Og II /// </summary> /// <param name="worldObjectPosition">Position in the world</param> /// <param name="sequence">Sequence for the object getting the message.</param> /// <param name="movementType">What type of movement are we about to execute</param> /// <param name="targetGuid">Who are we moving or turning toward</param> /// <returns>MovementStates</returns> public void OnAutonomousMove(Position worldObjectPosition, SequenceManager sequence, MovementTypes movementType, ObjectGuid targetGuid) { UniversalMotion newMotion = new UniversalMotion(MotionStance.Standing, worldObjectPosition, targetGuid); newMotion.DistanceFrom = 0.60f; newMotion.MovementTypes = movementType; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this)); CurrentLandblock.EnqueueBroadcastMotion(this, newMotion); }
public void HandleActionGetAndWieldItem(uint itemId, int wieldLocation) { new ActionChain(this, () => { var itemGuid = new ObjectGuid(itemId); if (TryRemoveFromInventory(itemGuid, out WorldObject item)) { if (!TryEquipObject(item, wieldLocation)) { log.Error("Player_Inventory HandleActionGetAndWieldItem TryEquipObject failed"); return; } if ((EquipMask)wieldLocation == EquipMask.MissileAmmo) { Session.Network.EnqueueSend( new GameEventWieldItem(Session, itemGuid.Full, wieldLocation), new GameMessageSound(Guid, Sound.WieldObject, 1.0f)); } else { if (((EquipMask)wieldLocation & EquipMask.Selectable) != 0) { SetChild(item, wieldLocation, out var placementId, out var childLocation); // todo I think we need to recalc our SetupModel here. see CalculateObjDesc() CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageParentEvent(Session.Player, item, childLocation, placementId), new GameEventWieldItem(Session, itemGuid.Full, wieldLocation), new GameMessageSound(Guid, Sound.WieldObject, 1.0f), new GameMessageUpdateInstanceId(Session.Player.Sequences, item.Guid, PropertyInstanceId.Container, new ObjectGuid(0)), new GameMessageUpdateInstanceId(Session.Player.Sequences, item.Guid, PropertyInstanceId.Wielder, Guid), new GameMessagePublicUpdatePropertyInt(Session.Player.Sequences, item.Guid, PropertyInt.CurrentWieldedLocation, wieldLocation)); if (CombatMode == CombatMode.NonCombat || CombatMode == CombatMode.Undef) { return; } switch ((EquipMask)wieldLocation) { case EquipMask.MissileWeapon: SetCombatMode(CombatMode.Missile); break; case EquipMask.Held: SetCombatMode(CombatMode.Magic); break; default: SetCombatMode(CombatMode.Melee); break; } }
public void HandleSwitchToMissileCombatMode(ActionChain combatModeChain) { 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 = WieldedObjects.First(s => s.Value.CurrentWieldedLocation == EquipMask.MissileAmmo).Value; MotionStance ms; if (missileWeapon.DefaultCombatStyle != null) { ms = (MotionStance)missileWeapon.DefaultCombatStyle; } else { log.InfoFormat("Changing combat mode for {0} - wielded item {1} has not be assigned a default combat style", Guid, mEquipedMissile.Guid); return; } 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, (uint)CombatMode.Missile)); } }
/// <summary> /// This method is called in response to a put item in container message. It is used when the item going into a container was wielded. /// It sets the appropriate properties, sends out response messages and handles switching stances - for example if you have a bow wielded and are in bow combat stance, /// when you unwield the bow, this also sends the messages needed to go into unarmed combat mode. Og II /// </summary> private void UnwieldItem(Container container, WorldObject item, int placement) { EquipMask?oldLocation = item.CurrentWieldedLocation; item.ContainerId = container.Guid.Full; item.SetPropertiesForContainer(placement); TryDequipObject(item.Guid); // todo I think we need to recalc our SetupModel here. see CalculateObjDesc() if ((oldLocation & EquipMask.Selectable) != 0) { // We are coming from a hand shield slot. Children.Remove(Children.Find(s => s.Guid == item.Guid.Full)); } // Set the container stuff item.ContainerId = container.Guid.Full; item.PlacementPosition = placement; ActionChain inContainerChain = new ActionChain(); inContainerChain.AddAction(this, () => { if (container.Guid != Guid) { container.AddToInventory(item, placement); //Burden += item.Burden; } else { AddToInventory(item, placement); } }); inContainerChain.EnqueueChain(); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdateInstanceId(Session.Player.Sequences, item.Guid, PropertyInstanceId.Wielder, new ObjectGuid(0)), new GameMessagePublicUpdatePropertyInt(Session.Player.Sequences, item.Guid, PropertyInt.CurrentWieldedLocation, 0), new GameMessageUpdateInstanceId(Session.Player.Sequences, item.Guid, PropertyInstanceId.Container, container.Guid), new GameMessagePickupEvent(item), new GameMessageSound(Guid, Sound.UnwieldObject, (float)1.0), new GameMessagePutObjectInContainer(Session, container.Guid, item, placement), new GameMessageObjDescEvent(this)); if ((oldLocation != EquipMask.MissileWeapon && oldLocation != EquipMask.Held && oldLocation != EquipMask.MeleeWeapon) || ((CombatMode & CombatMode.CombatCombat) == 0)) { return; } HandleSwitchToPeaceMode(CombatMode); HandleSwitchToMeleeCombatMode(CombatMode); }
/// <summary> /// Executes the weapon reload animation for the player /// </summary> public float ReloadMotion() { var reloadAnimation = new MotionItem(GetReloadAnimation(), 1.0f); var animLength = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, reloadAnimation); var motion = new UniversalMotion(CurrentMotionState.Stance); motion.MovementData.CurrentStyle = (uint)CurrentMotionState.Stance; motion.MovementData.ForwardCommand = (uint)MotionCommand.Reload; motion.MovementData.TurnSpeed = 2.25f; //motion.HasTarget = true; //motion.TargetGuid = target.Guid; CurrentMotionState = motion; var actionChain = new ActionChain(); actionChain.AddAction(this, () => DoMotion(motion)); actionChain.AddDelaySeconds(animLength); actionChain.AddAction(this, () => { motion.MovementData.ForwardCommand = (uint)MotionCommand.Invalid; DoMotion(motion); CurrentMotionState = motion; }); var ammo = GetEquippedAmmo(); if (ammo != null) { actionChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, new GameMessageParentEvent(this, ammo, (int)ACE.Entity.Enum.ParentLocation.RightHand, (int)ACE.Entity.Enum.Placement.RightHandCombat))); } actionChain.AddDelaySeconds(animLength); var player = this as Player; if (player != null) { actionChain.AddAction(this, () => player.Session.Network.EnqueueSend(new GameEventAttackDone(player.Session))); actionChain.AddAction(this, () => player.Session.Network.EnqueueSend(new GameEventCombatCommmenceAttack(player.Session))); // TODO: This gets rid of the hourglass but doesn't seem to be sent in retail pcaps... actionChain.AddAction(this, () => player.Session.Network.EnqueueSend(new GameEventAttackDone(player.Session))); } actionChain.EnqueueChain(); var weapon = GetEquippedWeapon(); var reloadTime = weapon.DefaultCombatStyle == CombatStyle.Crossbow ? 3.2f : 1.6f; return(animLength * reloadTime); }
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 = (ushort)MotionStance.Standing; SetMotionState(this, mm); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePrivateUpdatePropertyInt(Sequences, PropertyInt.CombatMode, (uint)CombatMode.NonCombat)); }
/// <summary> /// This method is called in response to a put item in container message. It is used when the item going into a container was wielded. /// It sets the appropriate properties, sends out response messages and handles switching stances - for example if you have a bow wielded and are in bow combat stance, /// when you unwield the bow, this also sends the messages needed to go into unarmed combat mode. Og II /// </summary> private void UnwieldItemWithNetworking(Container container, WorldObject item, int placement) { EquipMask?oldLocation = item.CurrentWieldedLocation; if (!TryDequipObject(item.Guid)) { log.Error("Player_Inventory UnwieldItemWithNetworking TryDequipObject failed"); return; } item.SetPropertiesForContainer(); if (!container.TryAddToInventory(item, placement)) { log.Error("Player_Inventory UnwieldItemWithNetworking TryAddToInventory failed"); return; } // If we've unwielded the item to a side pack, we must increment our main EncumbranceValue and Value if (container != this) { EncumbranceVal += item.EncumbranceVal; Value += item.Value; } if ((oldLocation & EquipMask.Selectable) != 0) { // We are coming from a hand shield slot. Children.Remove(Children.Find(s => s.Guid == item.Guid.Full)); } // todo I think we need to recalc our SetupModel here. see CalculateObjDesc() CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdateInstanceId(Session.Player.Sequences, item.Guid, PropertyInstanceId.Wielder, new ObjectGuid(0)), new GameMessagePublicUpdatePropertyInt(Session.Player.Sequences, item.Guid, PropertyInt.CurrentWieldedLocation, 0), new GameMessageUpdateInstanceId(Session.Player.Sequences, item.Guid, PropertyInstanceId.Container, container.Guid), new GameMessagePickupEvent(item), new GameMessageSound(Guid, Sound.UnwieldObject, (float)1.0), new GameMessagePutObjectInContainer(Session, container.Guid, item, placement), new GameMessageObjDescEvent(this)); if ((oldLocation != EquipMask.MissileWeapon && oldLocation != EquipMask.Held && oldLocation != EquipMask.MeleeWeapon) || ((CombatMode & CombatMode.CombatCombat) == 0)) { return; } HandleSwitchToPeaceMode(CombatMode); HandleSwitchToMeleeCombatMode(CombatMode); }
public void HandleSwitchToMagicCombatMode() { EquippedItem mEquipedWand = Children.Find(s => s.EquipMask == EquipMask.Held); if (mEquipedWand != null) { UniversalMotion mm = new UniversalMotion(MotionStance.Spellcasting); mm.MovementData.CurrentStyle = (ushort)MotionStance.Spellcasting; SetMotionState(this, mm); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePrivateUpdatePropertyInt(Sequences, PropertyInt.CombatMode, (uint)CombatMode.Magic)); } else { log.InfoFormat("Changing combat mode for {0} - could not locate a wielded magic caster", Guid); } }
/// <summary> /// This method will remove a worldobject if we have consumed all of the amount in the merge. /// This checks inventory or wielded items (you could be pulling stackable ammo out of a wielded slot and into a stack in your pack /// It then creates and sends the remove object message. Lastly, if the wo has ever been saved to the db, we clean up after ourselves. /// </summary> /// <param name="session">Session is used for sequence and target</param> /// <param name="fromWo">World object of the item are we merging from that needs to be destroyed.</param> public void RemoveWorldObject(Session session, WorldObject fromWo) { if (HasInventoryItem(fromWo.Guid)) { session.Player.TryRemoveFromInventory(fromWo.Guid); } GameMessageRemoveObject msgRemoveFrom = new GameMessageRemoveObject(fromWo); CurrentLandblock.EnqueueBroadcast(Location, MaxObjectTrackingRange, msgRemoveFrom); // todo fix for EF throw new NotImplementedException(); //if (fromWo.SnapShotOfAceObject().HasEverBeenSavedToDatabase) // DatabaseManager.Shard.DeleteObject(fromWo.SnapShotOfAceObject(), null); }
/// <summary> /// Updates the ammo count or destroys the ammo after launching the projectile. /// </summary> /// <param name="ammo">The missile ammo object</param> public void UpdateAmmoAfterLaunch(WorldObject ammo) { var player = this as Player; if (ammo.StackSize == 1) { TryDequipObject(ammo.Guid); player?.Session.Network.EnqueueSend(new GameMessageDeleteObject(ammo)); CurrentLandblock.EnqueueActionBroadcast(Location, Landblock.MaxObjectRange, p => p.StopTrackingObject(ammo, true)); } else { ammo.StackSize--; player?.Session.Network.EnqueueSend(new GameMessageSetStackSize(ammo)); CurrentLandblock.EnqueueBroadcast(Location, new GameMessagePickupEvent(ammo)); } }
private void Close(ObjectGuid closer = new ObjectGuid()) { if (CurrentMotionState == motionStateClosed) { return; } CurrentLandblock.EnqueueBroadcastMotion(this, motionClosed); CurrentMotionState = motionStateClosed; Ethereal = false; IsOpen = false; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePublicUpdatePropertyBool(Sequences, Guid, PropertyBool.Ethereal, Ethereal ?? false)); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePublicUpdatePropertyBool(Sequences, Guid, PropertyBool.Open, IsOpen ?? false)); if (closer.Full > 0) { UseTimestamp++; } }
private void Open(ObjectGuid opener = new ObjectGuid()) { if (CurrentMotionState == motionStateOpen) { return; } CurrentLandblock.EnqueueBroadcastMotion(this, motionOpen); CurrentMotionState = motionStateOpen; Ethereal = true; IsOpen = true; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePublicUpdatePropertyBool(Sequences, Guid, PropertyBool.Ethereal, Ethereal ?? true)); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePublicUpdatePropertyBool(Sequences, Guid, PropertyBool.Open, IsOpen ?? true)); if (opener.Full > 0) { UseTimestamp++; } }
/// <summary> /// Launches a missile attack from monster to target /// </summary> public void RangeAttack() { var dist = GetDistanceToTarget(); //Console.WriteLine("RangeAttack: " + dist); NextAttackTime = Timer.CurrentTime + MissileDelay; var weapon = GetEquippedWeapon(); var sound = weapon.DefaultCombatStyle == CombatStyle.Crossbow ? Sound.CrossbowRelease : Sound.BowRelease; if (CurrentLandblock != null) { CurrentLandblock.EnqueueBroadcast(Location, new GameMessageSound(Guid, sound, 1.0f)); } var player = AttackTarget as Player; var bodyPart = GetBodyPart(); float targetTime = 0.0f; var damageSource = LaunchProjectile(AttackTarget, out targetTime); var animLength = ReloadMotion(); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(targetTime); actionChain.AddAction(this, () => { var critical = false; var damageType = DamageType.Undef; var damage = CalculateDamage(ref damageType, bodyPart, ref critical); if (damage > 0.0f) { player.TakeDamage(this, damageType, damage, bodyPart, critical); } else { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"You evaded {Name}!", ChatMessageType.CombatEnemy)); } }); actionChain.EnqueueChain(); }
/// <summary> /// This method will remove a worldobject if we have consumed all of the amount in the merge. /// This checks inventory or wielded items (you could be pulling stackable ammo out of a wielded slot and into a stack in your pack /// It then creates and sends the remove object message. Lastly, if the wo has ever been saved to the db, we clean up after ourselves. /// </summary> /// <param name="session">Session is used for sequence and target</param> /// <param name="fromWo">World object of the item are we merging from that needs to be destroyed.</param> public void RemoveWorldObject(Session session, WorldObject fromWo) { if (HasItem(fromWo.Guid)) { session.Player.RemoveWorldObjectFromInventory(fromWo.Guid); } else { session.Player.RemoveFromWieldedObjects(fromWo.Guid); } GameMessageRemoveObject msgRemoveFrom = new GameMessageRemoveObject(fromWo); CurrentLandblock.EnqueueBroadcast(Location, MaxObjectTrackingRange, msgRemoveFrom); if (fromWo.SnapShotOfAceObject().HasEverBeenSavedToDatabase) { DatabaseManager.Shard.DeleteObject(fromWo.SnapShotOfAceObject(), null); } }
/// <summary> /// Launches a missile attack from player to target /// </summary> public void LaunchMissile(WorldObject target) { if (GetEquippedAmmo() == null || CombatMode == CombatMode.NonCombat) { return; } var creature = target as Creature; if (MissileTarget == null || creature.Health.Current <= 0) { MissileTarget = null; return; } var weapon = GetEquippedWeapon(); var sound = weapon.DefaultCombatStyle == CombatStyle.Crossbow ? Sound.CrossbowRelease : Sound.BowRelease; CurrentLandblock.EnqueueBroadcast(Location, new GameMessageSound(Guid, sound, 1.0f)); float targetTime = 0.0f; var damageSource = LaunchProjectile(target, out targetTime); // todo: get correct animlengths for shoot + reload + aim var animLength = ReloadMotion() * 2.5f; var actionChain = new ActionChain(); //actionChain.AddDelaySeconds(targetTime); //actionChain.AddAction(this, () => DamageTarget(target, damageSource)); if (creature.Health.Current > 0 && GetCharacterOption(CharacterOption.AutoRepeatAttacks)) { // reload animation, accuracy bar refill actionChain.AddDelaySeconds(animLength + AccuracyLevel); actionChain.AddAction(this, () => { LaunchMissile(target); }); actionChain.EnqueueChain(); } else { MissileTarget = null; } }
/// <summary> /// Sends object description if the client requests it /// </summary> /// <param name="item"></param> public void HandleActionForceObjDescSend(ObjectGuid item) { ActionChain objDescChain = new ActionChain(); objDescChain.AddAction(this, () => { WorldObject wo = GetInventoryItem(item); if (wo != null) { CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageObjDescEvent(wo)); } else { log.Debug($"Error - requested object description for an item I do not know about - {item.Full:X}"); } }); objDescChain.EnqueueChain(); }
/// <summary> /// This method is called if we unwield missle ammo. It will check to see if I have arrows wielded /// send the message to "hide" the arrow. /// </summary> /// <param name="oldCombatMode"></param> public void HandleUnloadMissileAmmo(CombatMode oldCombatMode) { // Before I can switch to any non missile mode, do I have missile ammo I need to remove? WorldObject ammo = null; HeldItem mEquipedAmmo = Children.Find(s => s.EquipMask == EquipMask.MissileAmmo); if (mEquipedAmmo != null) { ammo = GetInventoryItem(new ObjectGuid(mEquipedAmmo.Guid)); } if (oldCombatMode == CombatMode.Missile) { if (ammo != null) { ammo.Location = null; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePickupEvent(ammo)); } } }
/// <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 = WieldedObjects.FirstOrDefault(s => s.Value.CurrentWieldedLocation == EquipMask.MissileAmmo).Value; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePrivateUpdatePropertyInt(Sequences, PropertyInt.CombatMode, (uint)CombatMode.NonCombat)); if (mEquipedAmmo != null) { CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectGhostRange, new GameMessagePickupEvent(mEquipedAmmo)); } }
/// <summary> /// Used for unlocking a door via lockpick, so contains a skill check /// player.Skills[Skill.Lockpick].ActiveValue should be sent for the skill check /// </summary> public UnlockDoorResults UnlockDoor(uint playerLockpickSkillLvl) { if (ResistLockpick == 0) { return(UnlockDoorResults.CannotBePicked); } if (playerLockpickSkillLvl >= ResistLockpick) { if (!IsLocked ?? false) { return(UnlockDoorResults.AlreadyUnlocked); } IsLocked = false; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePublicUpdatePropertyBool(Sequences, Guid, PropertyBool.Locked, IsLocked ?? false)); CurrentLandblock.EnqueueBroadcastSound(this, Sound.LockSuccess); return(UnlockDoorResults.UnlockSuccess); } return(UnlockDoorResults.PickLockFailed); }
/// <summary> /// Used for unlocking a door via a key /// </summary> public UnlockDoorResults UnlockDoor(string keyCode) { if (IsOpen ?? false) { return(UnlockDoorResults.DoorOpen); } if (keyCode == LockCode) { if (!IsLocked ?? false) { return(UnlockDoorResults.AlreadyUnlocked); } IsLocked = false; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePublicUpdatePropertyBool(Sequences, Guid, PropertyBool.Locked, IsLocked ?? false)); CurrentLandblock.EnqueueBroadcastSound(this, Sound.LockSuccess); return(UnlockDoorResults.UnlockSuccess); } return(UnlockDoorResults.IncorrectKey); }
/// <summary> /// Launches a projectile from player to target /// </summary> public float LaunchProjectile(WorldObject target) { var ammo = GetEquippedAmmo(); var arrow = WorldObjectFactory.CreateNewWorldObject(ammo.WeenieClassId); var origin = Location.ToGlobal(); origin.Z += Height; var dest = target.Location.ToGlobal(); dest.Z += target.Height / GetAimHeight(target); var speed = 35.0f; var dir = Vector3.Normalize(dest - origin); origin += dir * 2.0f; arrow.Velocity = GetProjectileVelocity(target, origin, dir, dest, speed, out var time); var loc = Location; origin = Position.FromGlobal(origin).Pos; arrow.Location = new Position(loc.LandblockId.Raw, origin.X, origin.Y, origin.Z, loc.Rotation.X, loc.Rotation.Y, loc.Rotation.Z, loc.RotationW); SetProjectilePhysicsState(arrow); LandblockManager.AddObject(arrow); CurrentLandblock.EnqueueBroadcast(arrow.Location, new GameMessageScript(arrow.Guid, ACE.Entity.Enum.PlayScript.Launch, 1.0f)); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(time); actionChain.AddAction(arrow, () => Session.Network.EnqueueSend(new GameMessageSound(arrow.Guid, Sound.Collision, 1.0f))); actionChain.AddAction(arrow, () => CurrentLandblock.RemoveWorldObject(arrow.Guid, false)); actionChain.EnqueueChain(); return(time); }
private void Reset() { if ((Time.GetTimestamp() - UseTimestamp) < ResetInterval) { return; } if (!DefaultOpen) { Close(ObjectGuid.Invalid); if (DefaultLocked) { IsLocked = true; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePublicUpdatePropertyBool(Sequences, Guid, PropertyBool.Locked, IsLocked ?? true)); // CurrentLandblock.EnqueueBroadcastSound(this, Sound.LockSuccess); // TODO: need to find the lock sound } } else { Open(ObjectGuid.Invalid); } ResetTimestamp++; }
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 = GetWieldedItem(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 = GetEquippedAmmo(); 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; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this)); SetMotionState(this, mm); if (mEquipedAmmo != null) { 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); 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); // add to player tracking var wielder = CurrentLandblock.GetObject(new ObjectGuid(mEquipedAmmo.WielderId.Value)); combatModeChain.AddAction(this, () => CurrentLandblock.EnqueueActionBroadcast(Location, Landblock.MaxObjectRange, (Player p) => p.TrackObject(wielder))); combatModeChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageParentEvent(this, mEquipedAmmo, 1, 1))); } var player = this as Player; if (player != null) { player.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.Missile)); } } }
/// <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; }
public void HandleSwitchToMeleeCombatMode(CombatMode olCombatMode) { bool shieldEquiped = false; bool weaponInShieldSlot = false; // Check to see if we were in missile combat and have an arrow hanging around we might need to remove. HandleUnloadMissileAmmo(olCombatMode); EquippedItem mEquipedShieldSlot = Children.Find(s => s.EquipMask == EquipMask.Shield); if (mEquipedShieldSlot != null) { WorldObject itemInShieldSlot = GetInventoryItem(new ObjectGuid(mEquipedShieldSlot.Guid)); if (itemInShieldSlot != null) { if (itemInShieldSlot.ItemType == ItemType.Armor) { shieldEquiped = true; } else { weaponInShieldSlot = true; } } } EquippedItem mEquipedMelee = Children.Find(s => s.EquipMask == EquipMask.MeleeWeapon); EquippedItem mEquipedTwoHanded = Children.Find(s => s.EquipMask == EquipMask.TwoHanded); MotionStance ms = MotionStance.Invalid; // are we unarmed? If so, do we have a shield? if (mEquipedMelee == null && mEquipedTwoHanded == null && !weaponInShieldSlot) { if (!shieldEquiped) { ms = MotionStance.UaNoShieldAttack; } else { ms = MotionStance.MeleeShieldAttack; } } else if (weaponInShieldSlot) { ms = MotionStance.DualWieldAttack; } if (mEquipedTwoHanded != null) { WorldObject twoHandedWeapon = GetInventoryItem(new ObjectGuid(mEquipedTwoHanded.Guid)); if (twoHandedWeapon.DefaultCombatStyle != null) { ms = twoHandedWeapon.DefaultCombatStyle.Value; } } // Let's see if we are melee single handed / two handed with our without shield as appropriate. if (mEquipedMelee?.Guid != null && ms != MotionStance.DualWieldAttack) { WorldObject meleeWeapon = GetInventoryItem(new ObjectGuid(mEquipedMelee.Guid)); if (meleeWeapon == null) { log.InfoFormat("Changing combat mode for {0} - could not locate wielded weapon {1}", Guid, mEquipedMelee.Guid); return; } if (!shieldEquiped) { if (meleeWeapon.DefaultCombatStyle != null) { ms = meleeWeapon.DefaultCombatStyle.Value; } } else { switch (meleeWeapon.DefaultCombatStyle) { case MotionStance.MeleeNoShieldAttack: ms = MotionStance.MeleeShieldAttack; break; case MotionStance.ThrownWeaponAttack: ms = MotionStance.ThrownShieldCombat; break; case MotionStance.UaNoShieldAttack: ms = MotionStance.MeleeShieldAttack; break; default: log.InfoFormat( "Changing combat mode for {0} - unable to determine correct combat stance for weapon {1}", Guid, mEquipedMelee.Guid); return; } } } if (ms != MotionStance.Invalid) { UniversalMotion mm = new UniversalMotion(ms); mm.MovementData.CurrentStyle = (ushort)ms; SetMotionState(this, mm); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessagePrivateUpdatePropertyInt(Sequences, PropertyInt.CombatMode, (uint)CombatMode.Melee)); } else { log.InfoFormat("Changing combat mode for {0} - wielded item {1} has not be assigned a default combat style", Guid, mEquipedMelee?.Guid ?? mEquipedTwoHanded?.Guid); } }
/// <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> /// This method is used to pick items off the world - out of 3D space and into our inventory or to a wielded slot. /// It checks the use case needed, sends the appropriate response messages. In addition, it will move to objects /// that are out of range in the attemp to pick them up. It will call update apperiance if needed and you have /// wielded an item from the ground. Og II /// </summary> /// <param name="container"></param> /// <param name="itemGuid"></param> /// <param name="placement"></param> /// <param name="iidPropertyId"></param> private void PickupItem(Container container, ObjectGuid itemGuid, int placement, PropertyInstanceId iidPropertyId) { // Logical operations: // !! FIXME: How to handle repeat on condition? // while (!objectInRange) // try Move to object // !! FIXME: How to handle conditional // Try acquire from landblock // if acquire successful: // add to container ActionChain pickUpItemChain = new ActionChain(); // Move to the object pickUpItemChain.AddChain(CreateMoveToChain(itemGuid, PickUpDistance)); // Pick up the object // Start pickup animation pickUpItemChain.AddAction(this, () => { var motion = new UniversalMotion(MotionStance.Standing); motion.MovementData.ForwardCommand = (uint)MotionCommand.Pickup; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this), new GameMessageUpdateMotion(Guid, Sequences.GetCurrentSequence(SequenceType.ObjectInstance), Sequences, motion)); }); // Wait for animation to progress var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId); var pickupAnimationLength = motionTable.GetAnimationLength(MotionCommand.Pickup); pickUpItemChain.AddDelaySeconds(pickupAnimationLength); // Ask landblock to transfer item // pickUpItemChain.AddAction(CurrentLandblock, () => CurrentLandblock.TransferItem(itemGuid, containerGuid)); if (container.Guid.IsPlayer()) { CurrentLandblock.QueueItemTransfer(pickUpItemChain, itemGuid, container.Guid); } else { CurrentLandblock.ScheduleItemTransferInContainer(pickUpItemChain, itemGuid, (Container)GetInventoryItem(container.Guid)); } // Finish pickup animation pickUpItemChain.AddAction(this, () => { // If success, the item is in our inventory: WorldObject item = GetInventoryItem(itemGuid); if (item.ContainerId != Guid.Full) { //Burden += item.Burden ?? 0; if (item.WeenieType == WeenieType.Coin) { UpdateCurrencyClientCalculations(WeenieType.Coin); } } if (item is Container itemAsContainer) { Session.Network.EnqueueSend(new GameEventViewContents(Session, itemAsContainer)); foreach (var packItem in itemAsContainer.Inventory) { Session.Network.EnqueueSend(new GameMessageCreateObject(packItem.Value)); UpdateCurrencyClientCalculations(WeenieType.Coin); } } // Update all our stuff if we succeeded if (item != null) { item.SetPropertiesForContainer(placement); // FIXME(ddevec): I'm not 100% sure which of these need to be broadcasts, and which are local sends... var motion = new UniversalMotion(MotionStance.Standing); if (iidPropertyId == PropertyInstanceId.Container) { Session.Network.EnqueueSend( ////new GameMessagePrivateUpdatePropertyInt(Session.Player.Sequences, PropertyInt.EncumbranceVal, UpdateBurden()), new GameMessageSound(Guid, Sound.PickUpItem, 1.0f), new GameMessageUpdateInstanceId(itemGuid, container.Guid, iidPropertyId), new GameMessagePutObjectInContainer(Session, container.Guid, item, placement)); } else { AddToWieldedObjects(item, container, (EquipMask)placement); Session.Network.EnqueueSend(new GameMessageSound(Guid, Sound.WieldObject, (float)1.0), new GameMessageObjDescEvent(this), new GameMessageUpdateInstanceId(container.Guid, itemGuid, PropertyInstanceId.Wielder), new GameEventWieldItem(Session, itemGuid.Full, placement)); } CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdateMotion(Guid, Sequences.GetCurrentSequence(SequenceType.ObjectInstance), Sequences, motion), new GameMessagePickupEvent(item)); if (iidPropertyId == PropertyInstanceId.Wielder) { CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageObjDescEvent(this)); } // TODO: Og II - check this later to see if it is still required. Session.Network.EnqueueSend(new GameMessageUpdateObject(item)); } // If we didn't succeed, just stand up and be ashamed of ourself else { var motion = new UniversalMotion(MotionStance.Standing); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdateMotion(Guid, Sequences.GetCurrentSequence(SequenceType.ObjectInstance), Sequences, motion)); // CurrentLandblock.EnqueueBroadcast(self shame); } }); // Set chain to run pickUpItemChain.EnqueueChain(); }
/// <summary> /// Sends a death message broadcast all players on the landblock? that a killer has a victim /// </summary> /// <remarks> /// TODO: /// 1. Figure out who all receieves death messages, on what landblock and at what order - /// Example: Does the players around the vicitm receive the message or do the players at the lifestone receieve the message, or both? /// </remarks> /// <param name="deathMessage"></param> /// <param name="victimId"></param> public void ActionBroadcastKill(string deathMessage, ObjectGuid victimId, ObjectGuid killerId) { var deathBroadcast = new GameMessagePlayerKilled(deathMessage, victimId, killerId); CurrentLandblock.EnqueueBroadcast(Location, Landblock.OutdoorChatRange, deathBroadcast); }
public void DoFinishBarber(ClientMessage message) { // Read the payload sent from the client... PaletteBaseId = message.Payload.ReadUInt32(); HeadObject = message.Payload.ReadUInt32(); HairTexture = message.Payload.ReadUInt32(); DefaultHairTexture = message.Payload.ReadUInt32(); EyesTexture = message.Payload.ReadUInt32(); DefaultEyesTexture = message.Payload.ReadUInt32(); NoseTexture = message.Payload.ReadUInt32(); DefaultNoseTexture = message.Payload.ReadUInt32(); MouthTexture = message.Payload.ReadUInt32(); DefaultMouthTexture = message.Payload.ReadUInt32(); SkinPalette = message.Payload.ReadUInt32(); HairPalette = message.Payload.ReadUInt32(); EyesPalette = message.Payload.ReadUInt32(); SetupTableId = message.Payload.ReadUInt32(); uint option_bound = message.Payload.ReadUInt32(); // Supress Levitation - Empyrean Only uint option_unk = message.Payload.ReadUInt32(); // Unknown - Possibly set aside for future use? // Check if Character is Empyrean, and if we need to set/change/send new motion table if (Heritage == 9) { // These are the motion tables for Empyrean float and not-float (one for each gender). They are hard-coded into the client. const uint EmpyreanMaleFloatMotionDID = 0x0900020Bu; const uint EmpyreanFemaleFloatMotionDID = 0x0900020Au; const uint EmpyreanMaleMotionDID = 0x0900020Eu; const uint EmpyreanFemaleMotionDID = 0x0900020Du; // Check for the Levitation option for Empyrean. Shadow crown and Undead flames are handled by client. if (Gender == 1) // Male { if (option_bound == 1 && MotionTableId != EmpyreanMaleMotionDID) { MotionTableId = EmpyreanMaleMotionDID; Session.Network.EnqueueSend(new GameMessagePrivateUpdateDataID(this, PropertyDataId.MotionTable, (uint)MotionTableId)); } else if (option_bound == 0 && MotionTableId != EmpyreanMaleFloatMotionDID) { MotionTableId = EmpyreanMaleFloatMotionDID; Session.Network.EnqueueSend(new GameMessagePrivateUpdateDataID(this, PropertyDataId.MotionTable, (uint)MotionTableId)); } } else // Female { if (option_bound == 1 && MotionTableId != EmpyreanFemaleMotionDID) { MotionTableId = EmpyreanFemaleMotionDID; Session.Network.EnqueueSend(new GameMessagePrivateUpdateDataID(this, PropertyDataId.MotionTable, (uint)MotionTableId)); } else if (option_bound == 0 && MotionTableId != EmpyreanFemaleFloatMotionDID) { MotionTableId = EmpyreanFemaleFloatMotionDID; Session.Network.EnqueueSend(new GameMessagePrivateUpdateDataID(this, PropertyDataId.MotionTable, (uint)MotionTableId)); } } } // Broadcast updated character appearance CurrentLandblock.EnqueueBroadcast( Location, Landblock.MaxObjectRange, new GameMessageObjDescEvent(this)); }
/// <summary> /// Creates the Magic projectile spells for Life, War, and Void Magic /// </summary> /// <param name="caster"></param> /// <param name="target"></param> /// <param name="spellId"></param> /// <param name="projectileWcid"></param> /// <param name="lifeProjectileDamage"></param> private void CreateSpellProjectile(WorldObject caster, WorldObject target, uint spellId, uint projectileWcid, uint lifeProjectileDamage = 0) { SpellProjectile spellProjectile = WorldObjectFactory.CreateNewWorldObject(projectileWcid) as SpellProjectile; spellProjectile.Setup(spellId); var origin = caster.Location.ToGlobal(); if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc) { origin.Z += caster.Height; } else { origin.Z += caster.Height * 2.0f / 3.0f; } var dest = target.Location.ToGlobal(); dest.Z += target.Height / 2.0f; var direction = Vector3.Normalize(dest - origin); // This is not perfect but is close to values that retail used. TODO: revisit this later. origin += direction * (caster.PhysicsObj.GetRadius() + spellProjectile.PhysicsObj.GetRadius()); float time; var dist = (dest - origin).Length(); float speed = 15f; if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Bolt) { speed = GetStationaryVelocity(15f, dist); } else if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Streak) { speed = GetStationaryVelocity(45f, dist); } else if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc) { speed = GetStationaryVelocity(40f, dist); } // TODO: Implement target leading for non arc spells // Also: velocity seems to increase when target is moving away from the caster and decrease when // the target is moving toward the caster. This still needs more research. var velocity = direction * speed; if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc) { spellProjectile.Velocity = GetSpellProjectileVelocity(origin, dest, speed, out time); } else { spellProjectile.Velocity = new AceVector3(velocity.X, velocity.Y, velocity.Z); var velocityLength = spellProjectile.Velocity.Get().Length(); time = dist / velocityLength; } spellProjectile.FlightTime = time; var loc = caster.Location; origin = loc.Pos; if (spellProjectile.SpellType == SpellProjectile.ProjectileSpellType.Arc) { origin.Z += caster.Height; } else { origin.Z += caster.Height * 2.0f / 3.0f; } origin += direction * (caster.PhysicsObj.GetRadius() + spellProjectile.PhysicsObj.GetRadius()); spellProjectile.Location = new ACE.Entity.Position(loc.LandblockId.Raw, origin.X, origin.Y, origin.Z, loc.Rotation.X, loc.Rotation.Y, loc.Rotation.Z, loc.RotationW); spellProjectile.ParentWorldObject = (Creature)this; spellProjectile.TargetGuid = target.Guid; spellProjectile.LifeProjectileDamage = lifeProjectileDamage; LandblockManager.AddObject(spellProjectile); CurrentLandblock.EnqueueBroadcast(spellProjectile.Location, new GameMessageScript(spellProjectile.Guid, ACE.Entity.Enum.PlayScript.Launch, spellProjectile.PlayscriptIntensity)); // TODO : removed when real server projectile tracking and collisions are implemented var actionChain = new ActionChain(); actionChain.AddDelaySeconds(spellProjectile.FlightTime); actionChain.AddAction(spellProjectile, () => spellProjectile.HandleOnCollide(spellProjectile.TargetGuid)); actionChain.EnqueueChain(); }