public void ActOnJoin(ObjectGuid playerId) { if (active) return; active = true; Player player = CurrentLandblock?.GetObject(playerId) as Player; // team is either 0 or 1. -1 means failed to join var msgJoinResponse = new GameEventJoinGameResponse(player.Session, Guid.Full, 1); // 0 or 1 for winning team. -1 is used for stalemate, -2 (and gameId of 0) is used to exit game mode in client // var msgGameOver = new GameEventGameOver(player.Session, 0, -2); // player.Session.Network.EnqueueSend(msgJoinResponse, msgGameOver); player.Session.Network.EnqueueSend(msgJoinResponse); // 0xA9B2002E [135.97 133.313 94.4447] 1 0 0 0 (holtburg game location) // Drudges var drudgeRook1 = WorldObjectFactory.CreateNewWorldObject("drudgerook") as GamePiece; drudgeRook1.Location = new Position(Location.Cell, Location.PositionX - 3.5f, Location.PositionY - 3.5f, Location.PositionZ - 0.09788f, 0, 0, 0, 1); drudgeRook1.EnterWorld(); var drudgeKnight1 = WorldObjectFactory.CreateNewWorldObject("drudgeknight") as GamePiece; drudgeKnight1.Location = new Position(Location.Cell, Location.PositionX - 2.5f, Location.PositionY - 3.5f, Location.PositionZ - 0.09771f, 0, 0, 0, 1); drudgeKnight1.EnterWorld(); var drudgeBishop1 = WorldObjectFactory.CreateNewWorldObject("drudgebishop") as GamePiece; drudgeBishop1.Location = new Position(Location.Cell, Location.PositionX - 1.5f, Location.PositionY - 3.5f, Location.PositionZ - 0.09753f, 0, 0, 0, 1); drudgeBishop1.EnterWorld(); var drudgeQueen = WorldObjectFactory.CreateNewWorldObject("drudgequeen") as GamePiece; drudgeQueen.Location = new Position(Location.Cell, Location.PositionX - 0.5f, Location.PositionY - 3.5f, Location.PositionZ - 0.09735f, 0, 0, 0, 1); drudgeQueen.EnterWorld(); var drudgeKing = WorldObjectFactory.CreateNewWorldObject("drudgeking") as GamePiece; drudgeKing.Location = new Position(Location.Cell, Location.PositionX + 0.5f, Location.PositionY - 3.5f, Location.PositionZ - 0.09718f, 0, 0, 0, 1); drudgeKing.EnterWorld(); var drudgeBishop2 = WorldObjectFactory.CreateNewWorldObject("drudgebishop") as GamePiece; drudgeBishop2.Location = new Position(Location.Cell, Location.PositionX + 1.5f, Location.PositionY - 3.5f, Location.PositionZ - 0.09753f, 0, 0, 0, 1); drudgeBishop2.EnterWorld(); var drudgeKnight2 = WorldObjectFactory.CreateNewWorldObject("drudgeknight") as GamePiece; drudgeKnight2.Location = new Position(Location.Cell, Location.PositionX + 2.5f, Location.PositionY - 3.5f, Location.PositionZ - 0.09771f, 0, 0, 0, 1); drudgeKnight2.EnterWorld(); var drudgeRook2 = WorldObjectFactory.CreateNewWorldObject("drudgerook") as GamePiece; drudgeRook2.Location = new Position(Location.Cell, Location.PositionX + 3.5f, Location.PositionY - 3.5f, Location.PositionZ - 0.09788f, 0, 0, 0, 1); drudgeRook2.EnterWorld(); var drudgePawn1 = WorldObjectFactory.CreateNewWorldObject("drudgepawn") as GamePiece; drudgePawn1.Location = new Position(Location.Cell, Location.PositionX - 3.5f, Location.PositionY - 2.5f, Location.PositionZ - 0.09788f, 0, 0, 0, 1); drudgePawn1.EnterWorld(); var drudgePawn2 = WorldObjectFactory.CreateNewWorldObject("drudgepawn") as GamePiece; drudgePawn2.Location = new Position(Location.Cell, Location.PositionX - 2.5f, Location.PositionY - 2.5f, Location.PositionZ - 0.09771f, 0, 0, 0, 1); drudgePawn2.EnterWorld(); var drudgePawn3 = WorldObjectFactory.CreateNewWorldObject("drudgepawn") as GamePiece; drudgePawn3.Location = new Position(Location.Cell, Location.PositionX - 1.5f, Location.PositionY - 2.5f, Location.PositionZ - 0.09753f, 0, 0, 0, 1); drudgePawn3.EnterWorld(); var drudgePawn4 = WorldObjectFactory.CreateNewWorldObject("drudgepawn") as GamePiece; drudgePawn4.Location = new Position(Location.Cell, Location.PositionX - 0.5f, Location.PositionY - 2.5f, Location.PositionZ - 0.09735f, 0, 0, 0, 1); drudgePawn4.EnterWorld(); var drudgePawn5 = WorldObjectFactory.CreateNewWorldObject("drudgepawn") as GamePiece; drudgePawn5.Location = new Position(Location.Cell, Location.PositionX + 0.5f, Location.PositionY - 2.5f, Location.PositionZ - 0.09718f, 0, 0, 0, 1); drudgePawn5.EnterWorld(); var drudgePawn6 = WorldObjectFactory.CreateNewWorldObject("drudgepawn") as GamePiece; drudgePawn6.Location = new Position(Location.Cell, Location.PositionX + 1.5f, Location.PositionY - 2.5f, Location.PositionZ - 0.09753f, 0, 0, 0, 1); drudgePawn6.EnterWorld(); var drudgePawn7 = WorldObjectFactory.CreateNewWorldObject("drudgepawn") as GamePiece; drudgePawn7.Location = new Position(Location.Cell, Location.PositionX + 2.5f, Location.PositionY - 2.5f, Location.PositionZ - 0.09771f, 0, 0, 0, 1); drudgePawn7.EnterWorld(); var drudgePawn8 = WorldObjectFactory.CreateNewWorldObject("drudgepawn") as GamePiece; drudgePawn8.Location = new Position(Location.Cell, Location.PositionX + 3.5f, Location.PositionY - 2.5f, Location.PositionZ - 0.09788f, 0, 0, 0, 1); drudgePawn8.EnterWorld(); // Mosswarts var mosswartRook1 = WorldObjectFactory.CreateNewWorldObject("mosswartrook") as GamePiece; mosswartRook1.Location = new Position(Location.Cell, Location.PositionX - 3.5f, Location.PositionY + 3.5f, Location.PositionZ - 0.09788f, 0, 0, 1, 0); mosswartRook1.EnterWorld(); var mosswartKnight1 = WorldObjectFactory.CreateNewWorldObject("mosswartknight") as GamePiece; mosswartKnight1.Location = new Position(Location.Cell, Location.PositionX - 2.5f, Location.PositionY + 3.5f, Location.PositionZ - 0.09771f, 0, 0, 1, 0); mosswartKnight1.EnterWorld(); var mosswartBishop1 = WorldObjectFactory.CreateNewWorldObject("mosswartbishop") as GamePiece; mosswartBishop1.Location = new Position(Location.Cell, Location.PositionX - 1.5f, Location.PositionY + 3.5f, Location.PositionZ - 0.09753f, 0, 0, 1, 0); mosswartBishop1.EnterWorld(); var mosswartQueen = WorldObjectFactory.CreateNewWorldObject("mosswartqueen") as GamePiece; mosswartQueen.Location = new Position(Location.Cell, Location.PositionX - 0.5f, Location.PositionY + 3.5f, Location.PositionZ - 0.09735f, 0, 0, 1, 0); mosswartQueen.EnterWorld(); var mosswartKing = WorldObjectFactory.CreateNewWorldObject("mosswartking") as GamePiece; mosswartKing.Location = new Position(Location.Cell, Location.PositionX + 0.5f, Location.PositionY + 3.5f, Location.PositionZ - 0.09718f, 0, 0, 1, 0); mosswartKing.EnterWorld(); var mosswartBishop2 = WorldObjectFactory.CreateNewWorldObject("mosswartbishop") as GamePiece; mosswartBishop2.Location = new Position(Location.Cell, Location.PositionX + 1.5f, Location.PositionY + 3.5f, Location.PositionZ - 0.09753f, 0, 0, 1, 0); mosswartBishop2.EnterWorld(); var mosswartKnight2 = WorldObjectFactory.CreateNewWorldObject("mosswartknight") as GamePiece; mosswartKnight2.Location = new Position(Location.Cell, Location.PositionX + 2.5f, Location.PositionY + 3.5f, Location.PositionZ - 0.09771f, 0, 0, 1, 0); mosswartKnight2.EnterWorld(); var mosswartRook2 = WorldObjectFactory.CreateNewWorldObject("mosswartrook") as GamePiece; mosswartRook2.Location = new Position(Location.Cell, Location.PositionX + 3.5f, Location.PositionY + 3.5f, Location.PositionZ - 0.09788f, 0, 0, 1, 0); mosswartRook2.EnterWorld(); var mosswartPawn1 = WorldObjectFactory.CreateNewWorldObject("mosswartpawn") as GamePiece; mosswartPawn1.Location = new Position(Location.Cell, Location.PositionX - 3.5f, Location.PositionY + 2.5f, Location.PositionZ - 0.09788f, 0, 0, 1, 0); mosswartPawn1.EnterWorld(); var mosswartPawn2 = WorldObjectFactory.CreateNewWorldObject("mosswartpawn") as GamePiece; mosswartPawn2.Location = new Position(Location.Cell, Location.PositionX - 2.5f, Location.PositionY + 2.5f, Location.PositionZ - 0.09771f, 0, 0, 1, 0); mosswartPawn2.EnterWorld(); var mosswartPawn3 = WorldObjectFactory.CreateNewWorldObject("mosswartpawn") as GamePiece; mosswartPawn3.Location = new Position(Location.Cell, Location.PositionX - 1.5f, Location.PositionY + 2.5f, Location.PositionZ - 0.09753f, 0, 0, 1, 0); mosswartPawn3.EnterWorld(); var mosswartPawn4 = WorldObjectFactory.CreateNewWorldObject("mosswartpawn") as GamePiece; mosswartPawn4.Location = new Position(Location.Cell, Location.PositionX - 0.5f, Location.PositionY + 2.5f, Location.PositionZ - 0.09735f, 0, 0, 1, 0); mosswartPawn4.EnterWorld(); var mosswartPawn5 = WorldObjectFactory.CreateNewWorldObject("mosswartpawn") as GamePiece; mosswartPawn5.Location = new Position(Location.Cell, Location.PositionX + 0.5f, Location.PositionY + 2.5f, Location.PositionZ - 0.09718f, 0, 0, 1, 0); mosswartPawn5.EnterWorld(); var mosswartPawn6 = WorldObjectFactory.CreateNewWorldObject("mosswartpawn") as GamePiece; mosswartPawn6.Location = new Position(Location.Cell, Location.PositionX + 1.5f, Location.PositionY + 2.5f, Location.PositionZ - 0.09753f, 0, 0, 1, 0); mosswartPawn6.EnterWorld(); var mosswartPawn7 = WorldObjectFactory.CreateNewWorldObject("mosswartpawn") as GamePiece; mosswartPawn7.Location = new Position(Location.Cell, Location.PositionX + 2.5f, Location.PositionY + 2.5f, Location.PositionZ - 0.09771f, 0, 0, 1, 0); mosswartPawn7.EnterWorld(); var mosswartPawn8 = WorldObjectFactory.CreateNewWorldObject("mosswartpawn") as GamePiece; mosswartPawn8.Location = new Position(Location.Cell, Location.PositionX + 3.5f, Location.PositionY + 2.5f, Location.PositionZ - 0.09788f, 0, 0, 1, 0); mosswartPawn8.EnterWorld(); // For HellsWrath... ActionChain gdlChain = new ActionChain(); gdlChain.AddDelaySeconds(5); gdlChain.AddAction(this, () => { drudgeRook1.Kill(); drudgeBishop1.Kill(); drudgeKnight1.Kill(); drudgeQueen.Kill(); drudgeKing.Kill(); drudgeBishop2.Kill(); drudgeKnight2.Kill(); drudgeRook2.Kill(); drudgePawn1.Kill(); drudgePawn2.Kill(); drudgePawn3.Kill(); drudgePawn4.Kill(); drudgePawn5.Kill(); drudgePawn6.Kill(); drudgePawn7.Kill(); drudgePawn8.Kill(); mosswartRook1.Kill(); mosswartBishop1.Kill(); mosswartKnight1.Kill(); mosswartQueen.Kill(); mosswartKing.Kill(); mosswartBishop2.Kill(); mosswartKnight2.Kill(); mosswartRook2.Kill(); mosswartPawn1.Kill(); mosswartPawn2.Kill(); mosswartPawn3.Kill(); mosswartPawn4.Kill(); mosswartPawn5.Kill(); mosswartPawn6.Kill(); mosswartPawn7.Kill(); mosswartPawn8.Kill(); var msgGameOver = new GameEventGameOver(player.Session, Guid.Full, 0); player.Session.Network.EnqueueSend(msgGameOver); }); gdlChain.AddDelaySeconds(2); gdlChain.AddAction(this, () => { byte[] msg = Convert.FromBase64String("Z2FtZXNkZWFkbG9s"); var popupGDL = new GameEventPopupString(player.Session, System.Text.Encoding.UTF8.GetString(msg, 0, msg.Length)); var msgGameOver2 = new GameEventGameOver(player.Session, 0, -2); player.Session.Network.EnqueueSend(popupGDL, msgGameOver2); player.ChessGamesLost++; player.ChessTotalGames++; active = false; }); gdlChain.EnqueueChain(); }
List<ActionChain> DoAction(Player p, Board board, Func<List<Piece>,IEnumerable<ActionPair>> checkActionsFn, Action<Board,ActionPair> actionFn) { List<ActionChain> actions = new List<ActionChain> (4096); List<Piece> myPieces = new List<Piece> (); foreach (Piece pc in board.Pieces.Values) { if (pc.Owner == p) { myPieces.Add (pc); } } foreach (ActionPair move in checkActionsFn(myPieces)) { ActionChain chain = new ActionChain (); chain.actions.Add (move); Board moveBoard = Board.AIClone (board); actionFn (moveBoard, move); chain.Board = moveBoard; actions.Add (chain); } ActionChain skipChain = new ActionChain (); skipChain.actions.Add (new ActionPair () {action = ActionTaken.SKIPPED}); skipChain.Board = Board.AIClone (board); actions.Add (skipChain); return actions; }
public void ExecuteEmote(BiotaPropertiesEmote emote, BiotaPropertiesEmoteAction emoteAction, ActionChain actionChain, WorldObject sourceObject = null, WorldObject targetObject = null) { var player = targetObject as Player; switch ((EmoteType)emoteAction.Type) { case EmoteType.Say: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.CurrentLandblock.EnqueueBroadcast(sourceObject.Location, new GameMessageCreatureMessage(emoteAction.Message, sourceObject.Name, sourceObject.Guid.Full, ChatMessageType.Emote)); }); break; case EmoteType.Motion: if (emote.Category != (uint)EmoteCategory.Vendor && emote.Style != null) { var startingMotion = new UniversalMotion((MotionStance)emote.Style, new MotionItem((MotionCommand)emote.Substyle)); var motion = new UniversalMotion((MotionStance)emote.Style, new MotionItem((MotionCommand)emoteAction.Motion, emoteAction.Extent)); if (sourceObject.CurrentMotionState.Stance != startingMotion.Stance) { if (sourceObject.CurrentMotionState.Stance == MotionStance.Invalid) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.DoMotion(startingMotion); sourceObject.CurrentMotionState = startingMotion; }); } } else { if (sourceObject.CurrentMotionState.Commands.Count > 0 && sourceObject.CurrentMotionState.Commands[0].Motion == startingMotion.Commands[0].Motion) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.DoMotion(motion); sourceObject.CurrentMotionState = motion; }); actionChain.AddDelaySeconds(DatManager.PortalDat.ReadFromDat <DatLoader.FileTypes.MotionTable>(sourceObject.MotionTableId).GetAnimationLength((MotionCommand)emoteAction.Motion)); if (motion.Commands[0].Motion != MotionCommand.Sleeping && motion.Commands[0].Motion != MotionCommand.Sitting) // this feels like it can be handled better, somehow? { actionChain.AddAction(sourceObject, () => { sourceObject.DoMotion(startingMotion); sourceObject.CurrentMotionState = startingMotion; }); } } } } else { var motion = new UniversalMotion(MotionStance.Standing, new MotionItem((MotionCommand)emoteAction.Motion, emoteAction.Extent)); actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.DoMotion(motion); sourceObject.CurrentMotionState = motion; }); } break; case EmoteType.Tell: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { player.Session.Network.EnqueueSend(new GameMessageHearDirectSpeech(sourceObject, emoteAction.Message, player, ChatMessageType.Tell)); }); break; case EmoteType.TurnToTarget: actionChain.AddDelaySeconds(emoteAction.Delay); var creature = sourceObject is Creature ? (Creature)sourceObject : null; actionChain.AddAction(sourceObject, () => { creature.Rotate(player); }); actionChain.AddDelaySeconds(creature.GetRotateDelay(player)); break; case EmoteType.AwardXP: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { if (player != null) { player.EarnXP((long)emoteAction.Amount64); player.Session.Network.EnqueueSend(new GameMessageSystemChat("You've earned " + emoteAction.Amount64 + " experience.", ChatMessageType.System)); } }); break; case EmoteType.Give: actionChain.AddAction(sourceObject, () => { if (player != null) { uint weenie = (uint)emoteAction.WeenieClassId; WorldObject item = WorldObjectFactory.CreateNewWorldObject(weenie); if (emoteAction.WeenieClassId != null) { if (emoteAction.StackSize > 1) { item.StackSize = (ushort)emoteAction.StackSize; player.Session.Network.EnqueueSend(new GameMessageSystemChat(WorldObject.Name + " gives you " + emoteAction.StackSize + " " + item.Name + ".", ChatMessageType.System)); } else { player.Session.Network.EnqueueSend(new GameMessageSystemChat(WorldObject.Name + " gives you " + item.Name + ".", ChatMessageType.System)); } var success = player.TryCreateInInventoryWithNetworking(item); } } }); break; case EmoteType.UpdateQuest: //This is for the quest NPC's. This will be filled out when quests are added. break; case EmoteType.Turn: actionChain.AddDelaySeconds(emoteAction.Delay); creature = sourceObject is Creature ? (Creature)sourceObject : null; var pos = new Position(creature.Location.Cell, creature.Location.PositionX, creature.Location.PositionY, creature.Location.PositionZ, emoteAction.AnglesX ?? 0, emoteAction.AnglesY ?? 0, emoteAction.AnglesZ ?? 0, emoteAction.AnglesW ?? 0); actionChain.AddAction(sourceObject, () => { creature.TurnTo(pos); }); actionChain.AddDelaySeconds(creature.GetTurnToDelay(pos)); break; case EmoteType.Activate: creature = sourceObject is Creature ? (Creature)sourceObject : null; actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { if ((creature.ActivationTarget ?? 0) > 0) { var activationTarget = creature.CurrentLandblock.GetObject(new ObjectGuid(creature.ActivationTarget ?? 0)); activationTarget.ActOnUse(creature); } }); break; default: log.Debug($"EmoteManager.Execute - Encountered Unhandled EmoteType {(EmoteType)emoteAction.Type} for {sourceObject.Name} ({sourceObject.WeenieClassId})"); break; } }
/// <summary> /// Method used to perform the animation, sound, and vital update on consumption of food or potions /// </summary> /// <param name="consumableName">Name of the consumable</param> /// <param name="sound">Either Sound.Eat1 or Sound.Drink1</param> /// <param name="buffType">ConsumableBuffType.Spell,ConsumableBuffType.Health,ConsumableBuffType.Stamina,ConsumableBuffType.Mana</param> /// <param name="boostAmount">Amount the Vital is boosted by; can be null, if buffType = ConsumableBuffType.Spell</param> /// <param name="spellDID">Id of the spell cast by the consumable; can be null, if buffType != ConsumableBuffType.Spell</param> public void ApplyComsumable(string consumableName, Sound sound, ConsumableBuffType buffType, uint?boostAmount, uint?spellDID) { uint spellId = spellDID ?? 0; GameMessageSystemChat buffMessage; MotionCommand motionCommand; if (sound == Sound.Eat1) { motionCommand = MotionCommand.Eat; } else { motionCommand = MotionCommand.Drink; } var soundEvent = new GameMessageSound(Guid, sound, 1.0f); var motion = new UniversalMotion(MotionStance.NonCombat, new MotionItem(motionCommand)); EnqueueBroadcastMotion(motion); if (buffType == ConsumableBuffType.Spell) { bool result = false; if (spellId != 0) { result = CreateSingleSpell(spellId); } if (!result) { buffMessage = new GameMessageSystemChat($"Consuming {consumableName} attempted to apply a spell not yet fully implemented.", ChatMessageType.System); } else { buffMessage = new GameMessageSystemChat($"{consumableName} applies {DatManager.PortalDat.SpellTable.Spells[spellId].Name} on you.", ChatMessageType.Craft); } } else { CreatureVital creatureVital; string vitalName; // Null check for safety if (boostAmount == null) { boostAmount = 0; } switch (buffType) { case ConsumableBuffType.Health: creatureVital = Health; vitalName = "Health"; break; case ConsumableBuffType.Mana: creatureVital = Mana; vitalName = "Mana"; break; default: creatureVital = Stamina; vitalName = "Stamina"; break; } var vitalChange = UpdateVitalDelta(creatureVital, (uint)boostAmount); buffMessage = new GameMessageSystemChat($"You regain {vitalChange} {vitalName}.", ChatMessageType.Craft); } Session.Network.EnqueueSend(soundEvent, buffMessage); // Wait for animation var motionChain = new ActionChain(); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>((uint)MotionTableId); var motionAnimationLength = motionTable.GetAnimationLength(MotionCommand.Eat); motionChain.AddDelaySeconds(motionAnimationLength); // Return to standing position after the animation delay motionChain.AddAction(this, () => EnqueueBroadcastMotion(new UniversalMotion(MotionStance.NonCombat))); motionChain.EnqueueChain(); }
/// <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.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.UtcNow - item.ItemManaDepletionMessageTimestamp.Value).TotalSeconds > 120)) { item.ItemManaDepletionMessageTimestamp = DateTime.UtcNow; Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is low on Mana.", ChatMessageType.Magic)); } } } }
public override void OnUse(Session session) { bool success = true; string failReason = "You are unable to read the scroll."; switch (Power) { // research: http://asheron.wikia.com/wiki/Announcements_-_2002/06_-_Castling case spellLevel2: // Level 2 case spellLevel3: // Level 3 case spellLevel4: // Level 4 case spellLevel5: // Level 5 case spellLevel6: // Level 6 if (session.Player.CanReadScroll(School, Power)) { success = true; } else { success = false; failReason = "You are not skilled enough in the inscribed spell's school of magic to understand the writing on this scroll."; } break; default: // Level 1 or Level 7+ never fail success = true; break; } if (!session.Player.UnknownSpell(SpellId)) { success = false; failReason = "You already know the spell inscribed upon this scroll."; } ActionChain readScrollChain = new ActionChain(); readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReading)); readScrollChain.AddDelaySeconds(2); if (success) { readScrollChain.AddAction(session.Player, () => session.Player.LearnSpell(SpellId)); readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReady)); var removeObjMessage = new GameMessageRemoveObject(this); var destroyMessage = new GameMessageSystemChat("The scroll is destroyed.", ChatMessageType.Magic); readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(destroyMessage, removeObjMessage)); readScrollChain.AddAction(session.Player, () => session.Player.RemoveWorldObjectFromInventory(Guid)); } else { readScrollChain.AddDelaySeconds(2); readScrollChain.AddAction(session.Player, () => session.Player.HandleActionMotion(motionReady)); var failMessage = new GameMessageSystemChat($"{failReason}", ChatMessageType.Magic); readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(failMessage)); } var sendUseDoneEvent = new GameEventUseDone(session.Player.Session); readScrollChain.AddAction(session.Player, () => session.Network.EnqueueSend(sendUseDoneEvent)); readScrollChain.EnqueueChain(); }
public void InqCategory(EmoteCategory categoryId, BiotaPropertiesEmoteAction emoteAction, WorldObject sourceObject, WorldObject targetObject, ActionChain actionChain, bool useRNG = false) { var rng = useRNG ? Physics.Common.Random.RollDice(0.0f, 1.0f) : 0.0f; var result = sourceObject.Biota.BiotaPropertiesEmote.FirstOrDefault(e => e.Category == (uint)categoryId && e.Quest == emoteAction.Message && rng <= e.Probability); if (result == null) { return; } foreach (var action in result.BiotaPropertiesEmoteAction) { actionChain.AddAction(sourceObject, () => { ExecuteEmote(result, action, actionChain, sourceObject, targetObject); }); } }
public ActionChain(ActionChain link) { actions.AddRange (link.actions); }
private void FinalizeTrade(Player target) { if (!VerifyTrade_BusyState(target) || !VerifyTrade_Inventory(target)) { return; } IsBusy = true; target.IsBusy = true; TradeTransferInProgress = true; target.TradeTransferInProgress = true; Session.Network.EnqueueSend(new GameEventCommunicationTransientString(Session, "The items are being traded")); target.Session.Network.EnqueueSend(new GameEventCommunicationTransientString(target.Session, "The items are being traded")); var tradedItems = new Collection <(Biota biota, ReaderWriterLockSlim rwLock)>(); var myEscrow = new List <WorldObject>(); var targetEscrow = new List <WorldObject>(); foreach (ObjectGuid itemGuid in ItemsInTradeWindow) { if (TryRemoveFromInventoryWithNetworking(itemGuid, out var wo, RemoveFromInventoryAction.TradeItem) || TryDequipObjectWithNetworking(itemGuid, out wo, DequipObjectAction.TradeItem)) { targetEscrow.Add(wo); tradedItems.Add((wo.Biota, wo.BiotaDatabaseLock)); } } foreach (ObjectGuid itemGuid in target.ItemsInTradeWindow) { if (target.TryRemoveFromInventoryWithNetworking(itemGuid, out var wo, RemoveFromInventoryAction.TradeItem) || target.TryDequipObjectWithNetworking(itemGuid, out wo, DequipObjectAction.TradeItem)) { myEscrow.Add(wo); tradedItems.Add((wo.Biota, wo.BiotaDatabaseLock)); } } var actionChain = new ActionChain(); actionChain.AddDelaySeconds(0.5f); actionChain.AddAction(CurrentLandblock, () => { foreach (var wo in myEscrow) { TryCreateInInventoryWithNetworking(wo); } foreach (var wo in targetEscrow) { target.TryCreateInInventoryWithNetworking(wo); } Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.TradeComplete)); target.Session.Network.EnqueueSend(new GameEventWeenieError(target.Session, WeenieError.TradeComplete)); TradeTransferInProgress = false; target.TradeTransferInProgress = false; IsBusy = false; target.IsBusy = false; DatabaseManager.Shard.SaveBiotasInParallel(tradedItems, null); HandleActionResetTrade(Guid); target.HandleActionResetTrade(target.Guid); }); actionChain.EnqueueChain(); }
/// <summary> /// Broadcasts the player death animation, updates vitae, and sends network messages for player death /// Queues the action to call TeleportOnDeath and enter portal space soon /// </summary> protected override void Die(DamageHistoryInfo lastDamager, DamageHistoryInfo topDamager) { if (topDamager?.Guid == Guid && IsPKType) { var topDamagerOther = DamageHistory.GetTopDamager(false); if (topDamagerOther != null && topDamagerOther.IsPlayer) { topDamager = topDamagerOther; } } UpdateVital(Health, 0); NumDeaths++; suicideInProgress = false; // TODO: instead of setting IsBusy here, // eventually all of the places that check for states such as IsBusy || Teleporting // might want to use a common function, and IsDead should return a separate error IsBusy = true; // killer = top damager for looting rights if (topDamager != null) { KillerId = topDamager.Guid.Full; } // broadcast death animation var deathAnim = new Motion(MotionStance.NonCombat, MotionCommand.Dead); EnqueueBroadcastMotion(deathAnim); // create network messages for player death var msgHealthUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, 0); // TODO: death sounds? seems to play automatically in client // var msgDeathSound = new GameMessageSound(Guid, Sound.Death1, 1.0f); var msgNumDeaths = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.NumDeaths, NumDeaths); // send network messages for player death Session.Network.EnqueueSend(msgHealthUpdate, msgNumDeaths); if (lastDamager?.Guid == Guid) // suicide { var msgSelfInflictedDeath = new GameEventWeenieError(Session, WeenieError.YouKilledYourself); Session.Network.EnqueueSend(msgSelfInflictedDeath); } // update vitae // players who died in a PKLite fight do not accrue vitae if (!IsPKLiteDeath(topDamager)) { InflictVitaePenalty(); } if (IsPKDeath(topDamager) || AugmentationSpellsRemainPastDeath == 0) { var msgPurgeEnchantments = new GameEventMagicPurgeEnchantments(Session); EnchantmentManager.RemoveAllEnchantments(); Session.Network.EnqueueSend(msgPurgeEnchantments); } // wait for the death animation to finish var dieChain = new ActionChain(); var animLength = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.Dead); dieChain.AddDelaySeconds(animLength + 1.0f); dieChain.AddAction(this, () => { CreateCorpse(topDamager); ThreadSafeTeleportOnDeath(); // enter portal space SetLifestoneProtection(); if (IsPKDeath(topDamager) || IsPKLiteDeath(topDamager)) { SetMinimumTimeSincePK(); } IsBusy = false; }); dieChain.EnqueueChain(); }
/// <summary> /// Method used to perform the animation, sound, and vital update on consumption of food or potions /// </summary> /// <param name="consumableName">Name of the consumable</param> /// <param name="sound">Either Sound.Eat1 or Sound.Drink1</param> /// <param name="buffType">ConsumableBuffType.Spell,ConsumableBuffType.Health,ConsumableBuffType.Stamina,ConsumableBuffType.Mana</param> /// <param name="boostAmount">Amount the Vital is boosted by; can be null, if buffType = ConsumableBuffType.Spell</param> /// <param name="spellDID">Id of the spell cast by the consumable; can be null, if buffType != ConsumableBuffType.Spell</param> public void ApplyComsumable(string consumableName, Sound sound, ConsumableBuffType buffType, uint?boostAmount, uint?spellDID) { GameMessageSystemChat buffMessage; MotionCommand motionCommand; if (sound == Sound.Eat1) { motionCommand = MotionCommand.Eat; } else { motionCommand = MotionCommand.Drink; } var soundEvent = new GameMessageSound(Guid, sound, 1.0f); var motion = new UniversalMotion(MotionStance.Standing, new MotionItem(motionCommand)); DoMotion(motion); if (buffType == ConsumableBuffType.Spell) { // Null check for safety if (spellDID == null) { spellDID = 0; } // TODO: Handle spell cast buffMessage = new GameMessageSystemChat($"Consuming {consumableName} not yet fully implemented.", ChatMessageType.System); } else { CreatureVital creatureVital; string vitalName; // Null check for safety if (boostAmount == null) { boostAmount = 0; } switch (buffType) { case ConsumableBuffType.Health: creatureVital = Health; vitalName = "Health"; break; case ConsumableBuffType.Mana: creatureVital = Mana; vitalName = "Mana"; break; default: creatureVital = Stamina; vitalName = "Stamina"; break; } uint updatedVitalAmount = creatureVital.Current + (uint)boostAmount; if (updatedVitalAmount > creatureVital.MaxValue) { updatedVitalAmount = creatureVital.MaxValue; } boostAmount = updatedVitalAmount - creatureVital.Current; UpdateVital(creatureVital, updatedVitalAmount); buffMessage = new GameMessageSystemChat($"You regain {boostAmount} {vitalName}.", ChatMessageType.Craft); } Session.Network.EnqueueSend(soundEvent, buffMessage); // Wait for animation var motionChain = new ActionChain(); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>((uint)MotionTableId); var motionAnimationLength = motionTable.GetAnimationLength(MotionCommand.Eat); motionChain.AddDelaySeconds(motionAnimationLength); // Return to standing position after the animation delay motionChain.AddAction(this, () => DoMotion(new UniversalMotion(MotionStance.Standing))); motionChain.EnqueueChain(); }
public void LogOut_Inner(bool clientSessionTerminatedAbruptly = false) { IsLoggingOut = true; if (Fellowship != null) { FellowshipQuit(false); } if (IsTrading && TradePartner != null) { var tradePartner = PlayerManager.GetOnlinePlayer(TradePartner); if (tradePartner != null) { tradePartner.HandleActionCloseTradeNegotiations(); } } if (!clientSessionTerminatedAbruptly) { if (PropertyManager.GetBool("use_turbine_chat").Item) { if (GetCharacterOption(CharacterOption.ListenToGeneralChat)) { LeaveTurbineChatChannel("General"); } if (GetCharacterOption(CharacterOption.ListenToTradeChat)) { LeaveTurbineChatChannel("Trade"); } if (GetCharacterOption(CharacterOption.ListenToLFGChat)) { LeaveTurbineChatChannel("LFG"); } if (GetCharacterOption(CharacterOption.ListenToRoleplayChat)) { LeaveTurbineChatChannel("Roleplay"); } if (GetCharacterOption(CharacterOption.ListenToAllegianceChat) && Allegiance != null) { LeaveTurbineChatChannel("Allegiance"); } if (GetCharacterOption(CharacterOption.ListenToSocietyChat) && Society != FactionBits.None) { LeaveTurbineChatChannel("Society"); } } } if (CurrentActivePet != null) { CurrentActivePet.Destroy(); } if (CurrentLandblock != null) { var logout = new Motion(MotionStance.NonCombat, MotionCommand.LogOut); EnqueueBroadcastMotion(logout); EnqueueBroadcastPhysicsState(); var logoutChain = new ActionChain(); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>((uint)MotionTableId); float logoutAnimationLength = motionTable.GetAnimationLength(MotionCommand.LogOut); logoutChain.AddDelaySeconds(logoutAnimationLength); // remove the player from landblock management -- after the animation has run logoutChain.AddAction(this, () => { if (CurrentLandblock == null) { log.Debug($"0x{Guid}:{Name}.LogOut_Inner.logoutChain: CurrentLandblock is null, unable to remove from a landblock..."); if (Location != null) { log.Debug($"0x{Guid}:{Name}.LogOut_Inner.logoutChain: Location is not null, Location = {Location.ToLOCString()}"); } } CurrentLandblock?.RemoveWorldObject(Guid, false); SetPropertiesAtLogOut(); SavePlayerToDatabase(); PlayerManager.SwitchPlayerFromOnlineToOffline(this); }); // close any open landblock containers (chests / corpses) if (LastOpenedContainerId != ObjectGuid.Invalid) { var container = CurrentLandblock.GetObject(LastOpenedContainerId) as Container; if (container != null) { container.Close(this); } } logoutChain.EnqueueChain(); } else { log.Debug($"0x{Guid}:{Name}.LogOut_Inner: CurrentLandblock is null"); if (Location != null) { log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Location is not null, Location = {Location.ToLOCString()}"); var validLoadedLandblock = LandblockManager.GetLandblock(Location.LongObjCellID, false); if (validLoadedLandblock.GetObject(Guid.Full) != null) { log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Player is still on landblock, removing..."); validLoadedLandblock.RemoveWorldObject(Guid, false); } else { log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Player is not found on the landblock Location references."); } } else { log.Debug($"0x{Guid}:{Name}.LogOut_Inner: Location is null"); } SetPropertiesAtLogOut(); SavePlayerToDatabase(); PlayerManager.SwitchPlayerFromOnlineToOffline(this); } }
public void HandleActionEnterPkLite() { // ensure permanent npk if (PlayerKillerStatus != PlayerKillerStatus.NPK || MinimumTimeSincePk != null) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.OnlyNonPKsMayEnterPKLite)); return; } if (TooBusyToRecall) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YoureTooBusy)); return; } EnqueueBroadcast(new GameMessageSystemChat($"{Name} is looking for a fight!", ChatMessageType.Broadcast), LocalBroadcastRange); // perform pk lite entry motion / effect IsBusy = true; var prevStance = CurrentMotionState.Stance; var actionChain = new ActionChain(); var animTime = 0.0f; animTime += EnqueueMotion_Force(actionChain, MotionStance.NonCombat, MotionCommand.EnterPKLite); actionChain.AddAction(this, () => { if (PropertyManager.GetBool("allow_pkl_bump").Item) { // check for collisions PlayerKillerStatus = PlayerKillerStatus.PKLite; var colliding = PhysicsObj.ethereal_check_for_collisions(); if (colliding) { // try initial placement var result = PhysicsObj.SetPositionSimple(PhysicsObj.Position, true, Location.Instance); if (result == SetPositionError.OK) { // handle landblock update? SyncLocation(); // force broadcast Sequences.GetNextSequence(SequenceType.ObjectForcePosition); SendUpdatePosition(); } } } UpdateProperty(this, PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.PKLite, true); Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNowPKLite)); }); // return to previous stance, if applicable if (prevStance != MotionStance.NonCombat) { animTime += EnqueueMotion_Force(actionChain, prevStance, MotionCommand.Ready, MotionCommand.NonCombat); } actionChain.AddAction(this, () => IsBusy = false); actionChain.EnqueueChain(); }
/// <summary> /// Recursively called to determine the utility of every move available up to MAX_PLIES /// </summary> float MinMax(int depth, ActionChain continuesChain) { List<ActionChain> turnActions = PlayOneTurn (TurnTaker (depth), continuesChain.Board); NodesEvaluated += turnActions.Count; turnActions.ForEach (node => ScoringFn (depth, node)); if (Controlling == TurnTaker (depth)) { return turnActions.Max (node => node.score); } else { return turnActions.Min (node => node.score); } }
public void InqProperty(bool?prop, BiotaPropertiesEmoteAction emoteAction, WorldObject sourceObject, WorldObject targetObject, ActionChain actionChain) { var inRange = prop != null && prop.Value; InqPropertyInner(emoteAction, inRange, sourceObject, targetObject, actionChain); }
/// <summary> /// Determine a score by applying the scoring function, looking up the database, or recursing deeper. /// </summary> void ScoringFn(int depth, ActionChain node) { byte[] dbrec = new byte[4]; bool isTerminal = ApplyCutoff (node, depth); if (!isTerminal) { bool storedInDB = DB.Get (node.Board.GUID (), out dbrec); if (storedInDB) { node.score = BitConverter.ToSingle (dbrec, 0); } else { node.score = MinMax (depth + 1, node); // Store the score if it was obtained from a full search only if (depth == 0 && KeepSearching) { RecordNodeInDB (node.Board, node.score); } } } node.ClearBoard (); }
public void InqProperty(double?prop, BiotaPropertiesEmoteAction emoteAction, WorldObject sourceObject, WorldObject targetObject, ActionChain actionChain) { var inRange = prop != null && (prop.Value >= emoteAction.MinDbl && prop.Value <= emoteAction.MaxDbl); InqPropertyInner(emoteAction, inRange, sourceObject, targetObject, actionChain); }
public static void UseObjectOnTarget(Player player, WorldObject source, WorldObject target) { var recipe = DatabaseManager.World.GetCachedCookbook(source.WeenieClassId, target.WeenieClassId); if (recipe == null) { var message = new GameMessageSystemChat($"The {source.Name} cannot be used on the {target.Name}.", ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(); return; } ActionChain craftChain = new ActionChain(); CreatureSkill skill = null; bool skillSuccess = true; // assume success, unless there's a skill check double percentSuccess = 1; UniversalMotion motion = new UniversalMotion(MotionStance.Standing, new MotionItem(MotionCommand.ClapHands)); craftChain.AddAction(player, () => player.HandleActionMotion(motion)); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(player.MotionTableId); var craftAnimationLength = motionTable.GetAnimationLength(MotionCommand.ClapHands); craftChain.AddDelaySeconds(craftAnimationLength); craftChain.AddAction(player, () => { if (recipe.Recipe.Skill > 0 && recipe.Recipe.Difficulty > 0) { // there's a skill associated with this Skill skillId = (Skill)recipe.Recipe.Skill; // this shouldn't happen, but sanity check for unexpected nulls skill = player.GetCreatureSkill(skillId); if (skill == null) { log.Warn("Unexpectedly missing skill in Recipe usage"); player.SendUseDoneEvent(); return; } percentSuccess = skill.GetPercentSuccess(recipe.Recipe.Difficulty); //FIXME: Pretty certain this is broken } if (skill != null) { if (skill.AdvancementClass == SkillAdvancementClass.Untrained) { var message = new GameEventWeenieError(player.Session, WeenieError.YouAreNotTrainedInThatTradeSkill); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(WeenieError.YouAreNotTrainedInThatTradeSkill); return; } } // straight skill check, if applicable if (skill != null) { skillSuccess = _random.NextDouble() < percentSuccess; } if (skillSuccess) { bool destroyTarget = _random.NextDouble() < recipe.Recipe.SuccessDestroyTargetChance; bool destroySource = _random.NextDouble() < recipe.Recipe.SuccessDestroySourceChance; if (destroyTarget) { if (target.OwnerId == player.Guid.Full || player.GetInventoryItem(target.Guid) != null) { player.TryRemoveItemFromInventoryWithNetworking(target, (ushort)recipe.Recipe.SuccessDestroyTargetAmount); } else if (target.WielderId == player.Guid.Full) { if (!player.TryRemoveItemWithNetworking(target)) { throw new Exception($"Failed to remove {target.Name} from player inventory."); } } else { target.Destroy(); } if (!String.IsNullOrEmpty(recipe.Recipe.SuccessDestroyTargetMessage)) { var destroyMessage = new GameMessageSystemChat(recipe.Recipe.SuccessDestroyTargetMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(destroyMessage); } } if (destroySource) { if (source.OwnerId == player.Guid.Full || player.GetInventoryItem(target.Guid) != null) { player.TryRemoveItemFromInventoryWithNetworking(source, (ushort)recipe.Recipe.SuccessDestroySourceAmount); } else if (source.WielderId == player.Guid.Full) { if (!player.TryRemoveItemWithNetworking(source)) { throw new Exception($"Failed to remove {source.Name} from player inventory."); } } else { source.Destroy(); } if (!String.IsNullOrEmpty(recipe.Recipe.SuccessDestroySourceMessage)) { var destroyMessage = new GameMessageSystemChat(recipe.Recipe.SuccessDestroySourceMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(destroyMessage); } } if (recipe.Recipe.SuccessWCID > 0) { var wo = WorldObjectFactory.CreateNewWorldObject(recipe.Recipe.SuccessWCID); if (wo != null) { if (recipe.Recipe.SuccessAmount > 1) { wo.StackSize = (ushort)recipe.Recipe.SuccessAmount; } player.TryCreateInInventoryWithNetworking(wo); } } var message = new GameMessageSystemChat(recipe.Recipe.SuccessMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } else { bool destroyTarget = _random.NextDouble() < recipe.Recipe.FailDestroyTargetChance; bool destroySource = _random.NextDouble() < recipe.Recipe.FailDestroySourceChance; if (destroyTarget) { if (target.OwnerId == player.Guid.Full || player.GetInventoryItem(target.Guid) != null) { player.TryRemoveItemFromInventoryWithNetworking(target, (ushort)recipe.Recipe.FailDestroyTargetAmount); } else if (target.WielderId == player.Guid.Full) { if (!player.TryRemoveItemWithNetworking(target)) { throw new Exception($"Failed to remove {target.Name} from player inventory."); } } else { target.Destroy(); } if (!String.IsNullOrEmpty(recipe.Recipe.FailDestroyTargetMessage)) { var destroyMessage = new GameMessageSystemChat(recipe.Recipe.FailDestroyTargetMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(destroyMessage); } } if (destroySource) { if (source.OwnerId == player.Guid.Full || player.GetInventoryItem(target.Guid) != null) { player.TryRemoveItemFromInventoryWithNetworking(source, (ushort)recipe.Recipe.FailDestroySourceAmount); } else if (source.WielderId == player.Guid.Full) { if (!player.TryRemoveItemWithNetworking(source)) { throw new Exception($"Failed to remove {source.Name} from player inventory."); } } else { source.Destroy(); } if (!String.IsNullOrEmpty(recipe.Recipe.FailDestroySourceMessage)) { var destroyMessage = new GameMessageSystemChat(recipe.Recipe.FailDestroySourceMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(destroyMessage); } } if (recipe.Recipe.FailWCID > 0) { var wo = WorldObjectFactory.CreateNewWorldObject(recipe.Recipe.FailWCID); if (wo != null) { if (recipe.Recipe.FailAmount > 1) { wo.StackSize = (ushort)recipe.Recipe.FailAmount; } player.TryCreateInInventoryWithNetworking(wo); } } var message = new GameMessageSystemChat(recipe.Recipe.FailMessage, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } player.SendUseDoneEvent(); }); craftChain.EnqueueChain(); }
public void InqProperty(string prop, BiotaPropertiesEmoteAction emoteAction, WorldObject sourceObject, WorldObject targetObject, ActionChain actionChain) { if (prop == null) { return; } InqPropertyInner(emoteAction, true, sourceObject, targetObject, actionChain); }
/// <summary> /// Called on player login /// If a player has any skills trained that require updates from ACE-World-16-Patches, /// ensure these updates are installed, and if they aren't, send a helpful message to player with instructions for installation /// </summary> public void HandleDBUpdates() { // dirty fighting var dfSkill = GetCreatureSkill(Skill.DirtyFighting); if (dfSkill.AdvancementClass >= SkillAdvancementClass.Trained) { foreach (var spellID in SpellExtensions.DirtyFightingSpells) { var spell = new Server.Entity.Spell(spellID); if (spell.NotFound) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); actionChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameMessageSystemChat("To install Dirty Fighting, please apply the latest patches from https://github.com/ACEmulator/ACE-World-16PY-Patches", ChatMessageType.Broadcast)); }); actionChain.EnqueueChain(); } break; // performance improvement: only check first spell } } // void magic var voidSkill = GetCreatureSkill(Skill.VoidMagic); if (voidSkill.AdvancementClass >= SkillAdvancementClass.Trained) { foreach (var spellID in SpellExtensions.VoidMagicSpells) { var spell = new Server.Entity.Spell(spellID); if (spell.NotFound) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); actionChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameMessageSystemChat("To install Void Magic, please apply the latest patches from https://github.com/ACEmulator/ACE-World-16PY-Patches", ChatMessageType.Broadcast)); }); actionChain.EnqueueChain(); } break; // performance improvement: only check first spell (measured 102ms to check 75 uncached void spells) } } // summoning var summoning = GetCreatureSkill(Skill.Summoning); if (summoning.AdvancementClass >= SkillAdvancementClass.Trained) { uint essenceWCID = 48878; var weenie = DatabaseManager.World.GetCachedWeenie(essenceWCID); if (weenie == null) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); actionChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameMessageSystemChat("To install Summoning, please apply the latest patches from https://github.com/ACEmulator/ACE-World-16PY-Patches", ChatMessageType.Broadcast)); }); actionChain.EnqueueChain(); } } }
public void InqPropertyInner(BiotaPropertiesEmoteAction emoteAction, bool inRange, WorldObject sourceObject, WorldObject targetObject, ActionChain actionChain) { var category = inRange ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure; var useRNG = !inRange; // ?? InqCategory(category, emoteAction, sourceObject, targetObject, actionChain, useRNG); }
public static void HandleCharacterForcedDelete(Session session, params string[] parameters) { var characterName = string.Join(" ", parameters); var foundPlayer = PlayerManager.FindByName(characterName, out var isOnline); if (foundPlayer == null) { CommandHandlerHelper.WriteOutputInfo(session, $"There is no character named {characterName} in the database.", ChatMessageType.Broadcast); return; } if (isOnline && foundPlayer is Player player) { player.Character.DeleteTime = (ulong)Time.GetUnixTime(); player.Character.IsDeleted = true; player.CharacterChangesDetected = true; player.Session.LogOffPlayer(true); PlayerManager.HandlePlayerDelete(player.Character.Id); var success = PlayerManager.ProcessDeletedPlayer(player.Character.Id); if (success) { CommandHandlerHelper.WriteOutputInfo(session, $"Successfully {(isOnline ? "booted and " : "")}deleted character {foundPlayer.Name} (0x{foundPlayer.Guid}).", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to {(isOnline ? "boot and " : "")}delete character {foundPlayer.Name} (0x{foundPlayer.Guid}) due to PlayerManager failure.", ChatMessageType.Broadcast); } } else { var existingCharId = foundPlayer.Guid.Full; //DatabaseManager.Shard.BaseDatabase.GetCharacterStubByName(foundPlayer.Name).Id; DatabaseManager.Shard.GetCharacter(existingCharId, character => { if (character != null) { character.DeleteTime = (ulong)Time.GetUnixTime(); character.IsDeleted = true; DatabaseManager.Shard.SaveCharacter(character, new ReaderWriterLockSlim(), result => { if (result) { var deleteOfflineChain = new ActionChain(); deleteOfflineChain.AddAction(WorldManager.ActionQueue, () => PlayerManager.HandlePlayerDelete(character.Id)); deleteOfflineChain.AddDelayForOneTick(); deleteOfflineChain.AddAction(WorldManager.ActionQueue, () => { var success = PlayerManager.ProcessDeletedPlayer(character.Id); if (success) { CommandHandlerHelper.WriteOutputInfo(session, $"Successfully {(isOnline ? "booted and " : "")}deleted character {foundPlayer.Name} (0x{foundPlayer.Guid}).", ChatMessageType.Broadcast); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to {(isOnline ? "boot and " : "")}delete character {foundPlayer.Name} (0x{foundPlayer.Guid}) due to PlayerManager failure.", ChatMessageType.Broadcast); } }); deleteOfflineChain.EnqueueChain(); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to {(isOnline ? "boot and " : "")}delete character {foundPlayer.Name} (0x{foundPlayer.Guid}) due to shard database SaveCharacter failure.", ChatMessageType.Broadcast); } }); } else { CommandHandlerHelper.WriteOutputInfo(session, $"Unable to {(isOnline ? "boot and " : "")}delete character {foundPlayer.Name} (0x{foundPlayer.Guid}) due to shard database GetCharacter failure.", ChatMessageType.Broadcast); } }); } }
public void ExecuteEmote(BiotaPropertiesEmote emote, BiotaPropertiesEmoteAction emoteAction, ActionChain actionChain, WorldObject sourceObject = null, WorldObject targetObject = null) { var player = targetObject as Player; var creature = sourceObject as Creature; var targetCreature = targetObject as Creature; var emoteType = (EmoteType)emoteAction.Type; //if (emoteType != EmoteType.Motion && emoteType != EmoteType.Turn && emoteType != EmoteType.Move) //Console.WriteLine($"ExecuteEmote({emoteType})"); var text = emoteAction.Message; switch ((EmoteType)emoteAction.Type) { case EmoteType.Act: // short for 'acting' text var message = Replace(text, sourceObject, targetObject); sourceObject?.EnqueueBroadcast(new GameMessageSystemChat(message, ChatMessageType.Broadcast), 30.0f); break; case EmoteType.Activate: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { if (creature != null && (creature.ActivationTarget ?? 0) > 0) { var activationTarget = creature.CurrentLandblock?.GetObject(creature.ActivationTarget ?? 0); activationTarget?.ActOnUse(creature); } }); break; case EmoteType.AddCharacterTitle: // emoteAction.Stat == null for all EmoteType.AddCharacterTitle entries in current db? if (player != null) { player.AddTitle((CharacterTitle)emoteAction.Stat); } break; case EmoteType.AddContract: //if (player != null) //Contracts werent in emote table //player.AddContract(emoteAction.Stat); break; case EmoteType.AdminSpam: var players = WorldManager.GetAll(); foreach (var onlinePlayer in players) { onlinePlayer.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.AdminTell)); } break; case EmoteType.AwardLevelProportionalSkillXP: if (player != null) { player.GrantLevelProportionalSkillXP((Skill)emoteAction.Stat, emoteAction.Percent ?? 0, (ulong)emoteAction.Max); } break; case EmoteType.AwardLevelProportionalXP: if (player != null) { player.GrantLevelProportionalXp(emoteAction.Percent ?? 0, (ulong)emoteAction.Max); } break; case EmoteType.AwardLuminance: if (player != null) { player.GrantLuminance((long)emoteAction.Amount); } break; case EmoteType.AwardNoShareXP: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { if (player != null) { player.EarnXP((long)emoteAction.Amount64); player.Session.Network.EnqueueSend(new GameMessageSystemChat("You've earned " + emoteAction.Amount64 + " experience.", ChatMessageType.Broadcast)); } }); break; case EmoteType.AwardSkillPoints: if (player != null) { player.AwardSkillPoints((Skill)emoteAction.Stat, (uint)emoteAction.Amount, true); } break; case EmoteType.AwardSkillXP: if (player != null) { player.RaiseSkillGameAction((Skill)emoteAction.Stat, (uint)emoteAction.Amount, true); } break; case EmoteType.AwardTrainingCredits: if (player != null) { player.AddSkillCredits((int)emoteAction.Amount, true); } break; case EmoteType.AwardXP: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { if (player != null) { player.EarnXP((long)emoteAction.Amount64); player.Session.Network.EnqueueSend(new GameMessageSystemChat("You've earned " + emoteAction.Amount64 + " experience.", ChatMessageType.Broadcast)); } }); break; case EmoteType.BLog: // only one test drudge used this emoteAction. break; case EmoteType.CastSpell: if (WorldObject is Player) { (WorldObject as Player).CreatePlayerSpell((uint)emoteAction.SpellId); } else if (WorldObject is Creature) { (WorldObject as Creature).CreateCreatureSpell(player.Guid, (uint)emoteAction.SpellId); } break; case EmoteType.CastSpellInstant: var spellTable = DatManager.PortalDat.SpellTable; var spell = spellTable.Spells[(uint)emoteAction.SpellId]; actionChain.AddAction(sourceObject, () => { if (spell.TargetEffect > 0) { creature.CreateCreatureSpell(targetObject.Guid, (uint)emoteAction.SpellId); } else { creature.CreateCreatureSpell((uint)emoteAction.SpellId); } }); break; case EmoteType.CloseMe: targetObject.Close(WorldObject); break; case EmoteType.CreateTreasure: break; case EmoteType.DecrementIntStat: var id = (PropertyInt)emoteAction.Stat; var prop = player.GetProperty(id); if (prop != null) { player.SetProperty(id, prop.Value - 1); } break; case EmoteType.DecrementMyQuest: break; case EmoteType.DecrementQuest: // Used as part of the test drudge for events break; case EmoteType.DeleteSelf: sourceObject.CurrentLandblock?.RemoveWorldObject(sourceObject.Guid, false); break; case EmoteType.DirectBroadcast: text = Replace(emoteAction.Message, WorldObject, targetObject); if (player != null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); } break; case EmoteType.EraseMyQuest: break; case EmoteType.EraseQuest: if (player != null) { player.QuestManager.Erase(emoteAction.Message); } break; case EmoteType.FellowBroadcast: text = emoteAction.Message; if (player != null) { var fellowship = player.Fellowship; if (fellowship == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); } else { foreach (var fellow in fellowship.FellowshipMembers) { fellow.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); } } } break; case EmoteType.Generate: uint wcid = (uint)emoteAction.WeenieClassId; var item = WorldObjectFactory.CreateNewWorldObject((wcid)); break; case EmoteType.Give: bool success = false; if (player != null && emoteAction.WeenieClassId != null) { actionChain.AddAction(sourceObject, () => { item = WorldObjectFactory.CreateNewWorldObject((uint)emoteAction.WeenieClassId); var stackSize = emoteAction.StackSize ?? 1; var stackMsg = ""; if (stackSize > 1) { item.StackSize = (ushort)stackSize; stackMsg = stackSize + " "; // pluralize? } success = player.TryCreateInInventoryWithNetworking(item); // transaction / rollback on failure? if (success) { var msg = new GameMessageSystemChat($"{WorldObject.Name} gives you {stackMsg}{item.Name}.", ChatMessageType.Broadcast); var sound = new GameMessageSound(player.Guid, Sound.ReceiveItem, 1); player.Session.Network.EnqueueSend(msg, sound); } }); } break; case EmoteType.Goto: var rng = Physics.Common.Random.RollDice(0.0f, 1.0f); var firstEmote = sourceObject.Biota.BiotaPropertiesEmote.FirstOrDefault(e => e.Category == (uint)EmoteCategory.GotoSet && rng < e.Probability); foreach (var action in firstEmote.BiotaPropertiesEmoteAction) { actionChain.AddAction(player, () => { ExecuteEmote(firstEmote, action, actionChain, sourceObject, targetObject); }); } break; case EmoteType.IncrementIntStat: if (player == null || emoteAction.Stat == null) { break; } id = (PropertyInt)emoteAction.Stat; prop = player.GetProperty(id); if (prop != null) { player.SetProperty(id, prop.Value + 1); } break; case EmoteType.IncrementMyQuest: break; case EmoteType.IncrementQuest: if (player != null) { player.QuestManager.Increment(emoteAction.Message); } break; case EmoteType.InflictVitaePenalty: if (player != null) { player.VitaeCpPool++; // TODO: full path } break; case EmoteType.InqAttributeStat: if (targetCreature != null) { var attr = targetCreature.GetCreatureAttribute((PropertyAttribute)emoteAction.Stat); success = attr != null && attr.Ranks >= emoteAction.Min && attr.Ranks <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqBoolStat: // This is only used with NPC's 24944 and 6386, which are dev tester npc's. Not worth the current effort. // Could also be post-ToD break; case EmoteType.InqContractsFull: // not part of the game at PY16? //if (player != null) //{ // var contracts = player.TrackedContracts; // InqCategory(contracts.Count != 0 ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emote); //} break; case EmoteType.InqEvent: var started = EventManager.IsEventStarted(emoteAction.Message); InqCategory(started ? EmoteCategory.EventSuccess : EmoteCategory.EventFailure, emoteAction, sourceObject, targetObject, actionChain); break; case EmoteType.InqFellowNum: InqCategory(player != null && player.Fellowship != null ? EmoteCategory.TestSuccess : EmoteCategory.TestNoFellow, emoteAction, sourceObject, targetObject, actionChain); break; case EmoteType.InqFellowQuest: // focusing on 1 person quests to begin with break; case EmoteType.InqFloatStat: //InqProperty(target.GetProperty((PropertyFloat)emote.Stat), emote); break; case EmoteType.InqInt64Stat: //InqProperty(target.GetProperty((PropertyInt64)emote.Stat), emote); break; case EmoteType.InqIntStat: if (emoteAction.Stat != 25) { break; // ?? } success = player.Level >= emoteAction.Min && player.Level <= emoteAction.Max; // rng for failure case? var useRNG = !success; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain, useRNG); break; case EmoteType.InqMyQuest: break; case EmoteType.InqMyQuestBitsOff: break; case EmoteType.InqMyQuestBitsOn: break; case EmoteType.InqMyQuestSolves: break; case EmoteType.InqNumCharacterTitles: //if (player != null) //InqCategory(player.NumCharacterTitles != 0 ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emote); break; case EmoteType.InqOwnsItems: //if (player != null) //InqCategory(player.Inventory.Count > 0 ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emote); break; case EmoteType.InqPackSpace: //if (player != null) //{ // var freeSpace = player.ContainerCapacity > player.ItemCapacity; // InqCategory(freeSpace ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emote); //} break; case EmoteType.InqQuest: if (player != null) { var hasQuest = player.QuestManager.HasQuest(emoteAction.Message); InqCategory(hasQuest ? EmoteCategory.QuestSuccess : EmoteCategory.QuestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqQuestBitsOff: break; case EmoteType.InqQuestBitsOn: break; case EmoteType.InqQuestSolves: // should this be different from InqQuest? if (player != null) { var hasQuest = player.QuestManager.HasQuest(emoteAction.Message); InqCategory(hasQuest ? EmoteCategory.QuestSuccess : EmoteCategory.QuestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqRawAttributeStat: if (targetCreature != null) { var attr = targetCreature.GetCreatureAttribute((PropertyAttribute)emoteAction.Stat); success = attr != null && attr.Base >= emoteAction.Min && attr.Base <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqRawSecondaryAttributeStat: if (targetCreature != null) { var vital = targetCreature.GetCreatureVital((PropertyAttribute2nd)emoteAction.Stat); success = vital != null && vital.Base >= emoteAction.Min && vital.Base <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqRawSkillStat: if (targetCreature != null) { var skill = targetCreature.GetCreatureSkill((Skill)emoteAction.Stat); success = skill != null && skill.Base >= emoteAction.Min && skill.Base <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqSecondaryAttributeStat: if (targetCreature != null) { var vital = targetCreature.GetCreatureVital((PropertyAttribute2nd)emoteAction.Stat); success = vital != null && vital.Ranks >= emoteAction.Min && vital.Ranks <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqSkillSpecialized: if (targetCreature != null) { var skill = targetCreature.GetCreatureSkill((Skill)emoteAction.Stat); //InqProperty(skill.Status == SkillStatus.Specialized, emoteAction); } break; case EmoteType.InqSkillStat: if (targetCreature != null) { var skill = targetCreature.GetCreatureSkill((Skill)emoteAction.Stat); success = skill != null && skill.Ranks >= emoteAction.Min && skill.Ranks <= emoteAction.Max; InqCategory(success ? EmoteCategory.TestSuccess : EmoteCategory.TestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqSkillTrained: if (targetCreature != null) { var skill = targetCreature.GetCreatureSkill((Skill)emoteAction.Stat); // TestNoQuality? InqProperty(skill.AdvancementClass == SkillAdvancementClass.Trained || skill.AdvancementClass == SkillAdvancementClass.Specialized, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.InqStringStat: //InqProperty(targetCreature.GetProperty((PropertyString)emoteAction.Stat), emote); break; case EmoteType.InqYesNo: ConfirmationManager.ProcessConfirmation((uint)emoteAction.Stat, true); break; case EmoteType.Invalid: break; case EmoteType.KillSelf: if (targetCreature != null) { targetCreature.Smite(targetCreature); } break; case EmoteType.LocalBroadcast: if (actionChain != null) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject?.EnqueueBroadcast(new GameMessageCreatureMessage(emoteAction.Message, sourceObject.Name, sourceObject.Guid.Full, ChatMessageType.Broadcast)); }); } else { sourceObject.EnqueueBroadcast(new GameMessageCreatureMessage(emoteAction.Message, sourceObject.Name, sourceObject.Guid.Full, ChatMessageType.Broadcast)); } break; case EmoteType.LocalSignal: break; case EmoteType.LockFellow: if (player != null && player.Fellowship != null) { player.HandleActionFellowshipChangeOpenness(false); } break; case EmoteType.ForceMotion: // TODO: figure out the difference case EmoteType.Motion: if (sourceObject == null || sourceObject.CurrentMotionState == null) { break; } if (emote.Category != (uint)EmoteCategory.Vendor && emote.Style != null) { var startingMotion = new UniversalMotion((MotionStance)emote.Style, new MotionItem((MotionCommand)emote.Substyle)); var motion = new UniversalMotion((MotionStance)emote.Style, new MotionItem((MotionCommand)emoteAction.Motion, emoteAction.Extent)); if (sourceObject.CurrentMotionState.Stance != startingMotion.Stance) { if (sourceObject.CurrentMotionState.Stance == MotionStance.Invalid) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.EnqueueBroadcastMotion(startingMotion); sourceObject.CurrentMotionState = startingMotion; }); } } else { if (sourceObject.CurrentMotionState.Commands.Count > 0 && sourceObject.CurrentMotionState.Commands[0].Motion == startingMotion.Commands[0].Motion) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.EnqueueBroadcastMotion(motion); sourceObject.CurrentMotionState = motion; }); actionChain.AddDelaySeconds(DatManager.PortalDat.ReadFromDat <DatLoader.FileTypes.MotionTable>(sourceObject.MotionTableId).GetAnimationLength((MotionCommand)emoteAction.Motion)); if (motion.Commands[0].Motion != MotionCommand.Sleeping && motion.Commands[0].Motion != MotionCommand.Sitting) // this feels like it can be handled better, somehow? { actionChain.AddAction(sourceObject, () => { sourceObject.EnqueueBroadcastMotion(startingMotion); sourceObject.CurrentMotionState = startingMotion; }); } } } } else { var motion = new UniversalMotion(MotionStance.NonCombat, new MotionItem((MotionCommand)emoteAction.Motion, emoteAction.Extent)); actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject.EnqueueBroadcastMotion(motion); sourceObject.CurrentMotionState = motion; }); } break; case EmoteType.Move: // what is the difference between this and MoveToPos? // using MoveToPos logic for now... if (targetCreature != null) { var currentPos = targetCreature.Location; var newPos = new Position(); newPos.LandblockId = new LandblockId(currentPos.LandblockId.Raw); newPos.Pos = new Vector3(emoteAction.OriginX ?? currentPos.Pos.X, emoteAction.OriginY ?? currentPos.Pos.Y, emoteAction.OriginZ ?? currentPos.Pos.Z); if (emoteAction.AnglesX == null || emoteAction.AnglesY == null || emoteAction.AnglesZ == null || emoteAction.AnglesW == null) { newPos.Rotation = new Quaternion(currentPos.Rotation.X, currentPos.Rotation.Y, currentPos.Rotation.Z, currentPos.Rotation.W); } else { newPos.Rotation = new Quaternion(emoteAction.AnglesX ?? 0, emoteAction.AnglesY ?? 0, emoteAction.AnglesZ ?? 0, emoteAction.AnglesW ?? 1); } if (emoteAction.ObjCellId != null) { newPos.LandblockId = new LandblockId(emoteAction.ObjCellId.Value); } targetCreature.MoveTo(newPos, targetCreature.GetRunRate()); } break; case EmoteType.MoveHome: // TODO: call MoveToManager on server if (targetCreature != null) { targetCreature.MoveTo(targetCreature.Home, targetCreature.GetRunRate()); } break; case EmoteType.MoveToPos: if (targetCreature != null) { var currentPos = targetCreature.Location; var newPos = new Position(); newPos.LandblockId = new LandblockId(currentPos.LandblockId.Raw); newPos.Pos = new Vector3(emoteAction.OriginX ?? currentPos.Pos.X, emoteAction.OriginY ?? currentPos.Pos.Y, emoteAction.OriginZ ?? currentPos.Pos.Z); if (emoteAction.AnglesX == null || emoteAction.AnglesY == null || emoteAction.AnglesZ == null || emoteAction.AnglesW == null) { newPos.Rotation = new Quaternion(currentPos.Rotation.X, currentPos.Rotation.Y, currentPos.Rotation.Z, currentPos.Rotation.W); } else { newPos.Rotation = new Quaternion(emoteAction.AnglesX ?? 0, emoteAction.AnglesY ?? 0, emoteAction.AnglesZ ?? 0, emoteAction.AnglesW ?? 1); } if (emoteAction.ObjCellId != null) { newPos.LandblockId = new LandblockId(emoteAction.ObjCellId.Value); } targetCreature.MoveTo(newPos, targetCreature.GetRunRate()); } break; case EmoteType.OpenMe: sourceObject.Open(sourceObject); break; case EmoteType.PetCastSpellOnOwner: if (creature != null) { creature.CreateCreatureSpell(targetObject.Guid, (uint)emoteAction.SpellId); } break; case EmoteType.PhysScript: // TODO: landblock broadcast if (sourceObject != null) { sourceObject.PhysicsObj.play_script((PlayScript)emoteAction.PScript, 1.0f); } break; case EmoteType.PopUp: if (player != null) { if ((ConfirmationType)emoteAction.Stat == ConfirmationType.Undefined) { player.Session.Network.EnqueueSend(new GameEventPopupString(player.Session, emoteAction.Message)); } else { Confirmation confirm = new Confirmation((ConfirmationType)emoteAction.Stat, emoteAction.Message, sourceObject.Guid.Full, targetObject.Guid.Full); ConfirmationManager.AddConfirmation(confirm); player.Session.Network.EnqueueSend(new GameEventConfirmationRequest(player.Session, (ConfirmationType)emoteAction.Stat, confirm.ConfirmationID, confirm.Message)); } } break; case EmoteType.RemoveContract: if (player != null) { player.HandleActionAbandonContract((uint)emoteAction.Stat); } break; case EmoteType.RemoveVitaePenalty: if (player != null) { player.VitaeCpPool = 0; // TODO: call full path } break; case EmoteType.ResetHomePosition: //creature = sourceObject as Creature; //if (creature != null) // creature.Home = emoteAction.Position; break; case EmoteType.Say: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { sourceObject?.EnqueueBroadcast(new GameMessageCreatureMessage(emoteAction.Message, sourceObject.Name, sourceObject.Guid.Full, ChatMessageType.Emote)); }); break; case EmoteType.SetAltRacialSkills: break; case EmoteType.SetBoolStat: targetObject.SetProperty((PropertyBool)emoteAction.Stat, emoteAction.Amount == 0 ? false : true); break; case EmoteType.SetEyePalette: if (creature != null) { creature.EyesPaletteDID = (uint)emoteAction.Display; } break; case EmoteType.SetEyeTexture: if (creature != null) { creature.EyesTextureDID = (uint)emoteAction.Display; } break; case EmoteType.SetFloatStat: targetObject.SetProperty((PropertyFloat)emoteAction.Stat, (float)emoteAction.Amount); break; case EmoteType.SetHeadObject: if (creature != null) { creature.HeadObjectDID = (uint)emoteAction.Display; } break; case EmoteType.SetHeadPalette: break; case EmoteType.SetInt64Stat: player.SetProperty((PropertyInt)emoteAction.Stat, (int)emoteAction.Amount); break; case EmoteType.SetIntStat: player.SetProperty((PropertyInt)emoteAction.Stat, (int)emoteAction.Amount); break; case EmoteType.SetMouthPalette: break; case EmoteType.SetMouthTexture: creature = sourceObject as Creature; if (creature != null) { creature.MouthTextureDID = (uint)emoteAction.Display; } break; case EmoteType.SetMyQuestBitsOff: break; case EmoteType.SetMyQuestBitsOn: break; case EmoteType.SetMyQuestCompletions: break; case EmoteType.SetNosePalette: break; case EmoteType.SetNoseTexture: creature = sourceObject as Creature; if (creature != null) { creature.NoseTextureDID = (uint)emoteAction.Display; } break; case EmoteType.SetQuestBitsOff: break; case EmoteType.SetQuestBitsOn: break; case EmoteType.SetQuestCompletions: break; case EmoteType.SetSanctuaryPosition: //if (player != null) //player.Sanctuary = emote.Position; break; case EmoteType.Sound: targetObject.EnqueueBroadcast(new GameMessageSound(targetObject.Guid, (Sound)emoteAction.Sound, 1.0f)); break; case EmoteType.SpendLuminance: if (player != null) { player.SpendLuminance((long)emoteAction.Amount); } break; case EmoteType.StampFellowQuest: break; case EmoteType.StampMyQuest: break; case EmoteType.StampQuest: // work needs to be done here if (player != null) { player.QuestManager.Add(emoteAction.Message); } break; case EmoteType.StartBarber: break; case EmoteType.StartEvent: EventManager.StartEvent(emoteAction.Message); break; case EmoteType.StopEvent: EventManager.StopEvent(emoteAction.Message); break; case EmoteType.TakeItems: if (player != null && emoteAction.WeenieClassId != null) { item = WorldObjectFactory.CreateNewWorldObject((uint)emoteAction.WeenieClassId); if (item == null) { break; } success = player.TryRemoveItemFromInventoryWithNetworking(item, (ushort)emoteAction.Amount); } break; case EmoteType.TeachSpell: if (player != null) { player.LearnSpellWithNetworking((uint)emoteAction.SpellId); } break; case EmoteType.TeleportSelf: //if (WorldObject is Player) //(WorldObject as Player).Teleport(emote.Position); break; case EmoteType.TeleportTarget: //if (player != null) //player.Teleport(emote.Position); break; case EmoteType.Tell: actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { player.Session.Network.EnqueueSend(new GameMessageHearDirectSpeech(sourceObject, emoteAction.Message, player, ChatMessageType.Tell)); }); break; case EmoteType.TellFellow: text = emoteAction.Message; if (player != null) { var fellowship = player.Fellowship; if (fellowship == null) { player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Tell)); } else { foreach (var fellow in fellowship.FellowshipMembers) { fellow.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Tell)); } } } break; case EmoteType.TextDirect: if (player != null) { // should these delays be moved to 1 place?? actionChain.AddDelaySeconds(emoteAction.Delay); text = emoteAction.Message; // no known instances of replace tokens in current text, but could be added in future actionChain.AddAction(player, () => { player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Broadcast)); }); } break; case EmoteType.Turn: if (creature != null) { actionChain.AddDelaySeconds(emoteAction.Delay); var pos = new Position(creature.Location.Cell, creature.Location.PositionX, creature.Location.PositionY, creature.Location.PositionZ, emoteAction.AnglesX ?? 0, emoteAction.AnglesY ?? 0, emoteAction.AnglesZ ?? 0, emoteAction.AnglesW ?? 0); actionChain.AddAction(creature, () => { creature.TurnTo(pos); }); var rotateTime = creature.GetRotateDelay(pos); actionChain.AddDelaySeconds(rotateTime); } break; case EmoteType.TurnToTarget: if (creature != null && targetCreature != null) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(creature, () => { creature.Rotate(targetCreature); }); var rotateTime = creature.GetRotateDelay(targetCreature); actionChain.AddDelaySeconds(rotateTime); } break; case EmoteType.UntrainSkill: if (player != null) { player.UntrainSkill((Skill)emoteAction.Stat, 1); } break; case EmoteType.UpdateFellowQuest: break; case EmoteType.UpdateMyQuest: break; case EmoteType.UpdateQuest: // only delay seems to be with test NPC here // still, unsafe to use any emotes directly outside of a chain, // as they could be executed out-of-order if (player != null) { var questName = emoteAction.Message; player.QuestManager.Add(questName); var hasQuest = player.QuestManager.HasQuest(questName); InqCategory(hasQuest ? EmoteCategory.QuestSuccess : EmoteCategory.QuestFailure, emoteAction, sourceObject, targetObject, actionChain); } break; case EmoteType.WorldBroadcast: if (player != null) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { player.Session.Network.EnqueueSend(new GameMessageHearDirectSpeech(sourceObject, emoteAction.Message, player, ChatMessageType.WorldBroadcast)); }); } break; default: log.Debug($"EmoteManager.Execute - Encountered Unhandled EmoteType {(EmoteType)emoteAction.Type} for {sourceObject.Name} ({sourceObject.WeenieClassId})"); break; } }
/// <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(); }
/// <summary> /// Recalls you to your allegiance's Mansion or Villa /// </summary> public void HandleActionTeleToMansion() { //Console.WriteLine($"{Name}.HandleActionTeleToMansion()"); if (RecallsDisabled) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.ExitTrainingAcademyToUseCommand)); return; } // check if player is in an allegiance if (Allegiance == null) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNotInAllegiance)); return; } var allegianceHouse = Allegiance.GetHouse(); if (allegianceHouse == null) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YourMonarchDoesNotOwnAMansionOrVilla)); return; } if (allegianceHouse.HouseType < ACE.Entity.Enum.HouseType.Villa) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YourMonarchsHouseIsNotAMansionOrVilla)); return; } // ensure allegiance housing has allegiance permissions enabled if (allegianceHouse.MonarchId == null) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YourMonarchHasClosedTheMansion)); return; } if (CombatMode != CombatMode.NonCombat) { // this should be handled by a different thing, probably a function that forces player into peacemode var updateCombatMode = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.NonCombat); SetCombatMode(CombatMode.NonCombat); Session.Network.EnqueueSend(updateCombatMode); } EnqueueBroadcast(new GameMessageSystemChat($"{Name} is recalling to the Allegiance housing.", ChatMessageType.Recall), 96.0f); EnqueueBroadcastMotion(motionHouseRecall); var startPos = new Position(Location); // Wait for animation var actionChain = new ActionChain(); // Then do teleport var animLength = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.HouseRecall); actionChain.AddDelaySeconds(animLength); actionChain.AddAction(this, () => { var endPos = new Position(Location); if (startPos.SquaredDistanceTo(endPos) > RecallMoveThresholdSq) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouHaveMovedTooFar)); return; } Teleport(allegianceHouse.SlumLord.Location); }); actionChain.EnqueueChain(); }
public void PlayerEnterWorld() { PlayerManager.SwitchPlayerFromOfflineToOnline(this); Teleporting = true; // Save the the LoginTimestamp var lastLoginTimestamp = Time.GetUnixTime(); LoginTimestamp = lastLoginTimestamp; LastTeleportStartTimestamp = lastLoginTimestamp; Character.LastLoginTimestamp = lastLoginTimestamp; Character.TotalLogins++; CharacterChangesDetected = true; Sequences.SetSequence(SequenceType.ObjectInstance, new UShortSequence((ushort)Character.TotalLogins)); if (BarberActive) { BarberActive = false; } if (AllegianceNode != null) { AllegianceRank = (int)AllegianceNode.Rank; } else { AllegianceRank = null; } if (!Account15Days) { var accountTimeSpan = DateTime.UtcNow - Account.CreateTime; if (accountTimeSpan.TotalDays >= 15) { Account15Days = true; } } if (PlayerKillerStatus == PlayerKillerStatus.PKLite && !PropertyManager.GetBool("pkl_server").Item) { PlayerKillerStatus = PlayerKillerStatus.NPK; var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); actionChain.AddAction(this, () => { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNonPKAgain)); }); actionChain.EnqueueChain(); } // SendSelf will trigger the entrance into portal space SendSelf(); // Update or override certain properties sent to client. // bugged: do not send this here, or else a freshly loaded acclient will overrwrite the values // wait until first enter world is completed //SendPropertyUpdatesAndOverrides(); if (PropertyManager.GetBool("use_turbine_chat").Item) { // Init the client with the chat channel ID's, and then notify the player that they've joined the associated channels. Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.TurbineChatIsEnabled)); if (GetCharacterOption(CharacterOption.ListenToAllegianceChat) && Allegiance != null) { JoinTurbineChatChannel("Allegiance"); } if (GetCharacterOption(CharacterOption.ListenToGeneralChat)) { JoinTurbineChatChannel("General"); } if (GetCharacterOption(CharacterOption.ListenToTradeChat)) { JoinTurbineChatChannel("Trade"); } if (GetCharacterOption(CharacterOption.ListenToLFGChat)) { JoinTurbineChatChannel("LFG"); } if (GetCharacterOption(CharacterOption.ListenToRoleplayChat)) { JoinTurbineChatChannel("Roleplay"); } if (GetCharacterOption(CharacterOption.ListenToSocietyChat) && Society != FactionBits.None) { JoinTurbineChatChannel("Society"); } } // check if vassals earned XP while offline HandleAllegianceOnLogin(); HandleHouseOnLogin(); // retail appeared to send the squelch list very early, // even before the CreatePlayer, but doing it here if (SquelchManager.HasSquelches) { SquelchManager.SendSquelchDB(); } AuditItemSpells(); AuditEquippedItems(); HandleMissingXp(); HandleSkillCreditRefund(); HandleSkillTemplesReset(); HandleSkillSpecCreditRefund(); HandleFreeSkillResetRenewal(); HandleFreeAttributeResetRenewal(); HandleDBUpdates(); if (ServerManager.ShutdownInitiated) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(10.0f); actionChain.AddAction(this, () => { SendMessage(ServerManager.ShutdownNoticeText(), ChatMessageType.WorldBroadcast); }); actionChain.EnqueueChain(); } }
/// <summary> /// Handles the eviction process for a player house /// </summary> public static void HandleEviction(House house, uint playerGuid, bool multihouse = false, bool force = false) { // clear out slumlord inventory var slumlord = house.SlumLord; slumlord.ClearInventory(true); var player = PlayerManager.FindByGuid(playerGuid, out bool isOnline); if (!PropertyManager.GetBool("house_rent_enabled", true).Item&& !multihouse && !force) { // rent disabled, push forward var purchaseTime = (uint)(player.HousePurchaseTimestamp ?? 0); var nextRentTime = house.GetRentDue(purchaseTime); player.HouseRentTimestamp = (int)nextRentTime; log.Debug($"[HOUSE] HouseManager.HandleRentPaid({player.Name}): house rent disabled via config"); // re-add item to queue AddRentQueue(player, house); return; } // handle eviction house.HouseOwner = null; house.MonarchId = null; house.HouseOwnerName = null; house.ClearPermissions(); house.SaveBiotaToDatabase(); // relink house.UpdateLinks(); if (house.HasDungeon) { var dungeonHouse = house.GetDungeonHouse(); if (dungeonHouse != null) { dungeonHouse.UpdateLinks(); } } // player slumlord 'off' animation slumlord.Off(); // reset slumlord name slumlord.SetAndBroadcastName(); slumlord.SaveBiotaToDatabase(); // if evicting a multihouse owner's previous house, // no update for player properties if (player.HouseInstance == house.Guid.Full) { player.HouseId = null; player.HouseInstance = null; //player.HousePurchaseTimestamp = null; player.HouseRentTimestamp = null; } else { log.Warn($"[HOUSE] HouseManager.HandleRentEviction({house.Guid}, {player.Name}, {multihouse}): house guids don't match {player.HouseInstance}"); } house.ClearRestrictions(); log.Debug($"[HOUSE] HouseManager.HandleRentEviction({player.Name})"); if (multihouse) { RemoveRentQueue(house.Guid.Full); player.SaveBiotaToDatabase(); return; } if (!isOnline) { // inform player of eviction when they log in var offlinePlayer = PlayerManager.GetOfflinePlayer(playerGuid); if (offlinePlayer == null) { log.Warn($"[HOUSE] {player.Name}.HandleEviction(): couldn't find offline player"); return; } offlinePlayer.SetProperty(PropertyBool.HouseEvicted, true); offlinePlayer.SaveBiotaToDatabase(); return; } var onlinePlayer = PlayerManager.GetOnlinePlayer(playerGuid); onlinePlayer.House = null; // send text message onlinePlayer.Session.Network.EnqueueSend(new GameMessageSystemChat("You've been evicted from your house!", ChatMessageType.Broadcast)); onlinePlayer.RemoveDeed(); onlinePlayer.SaveBiotaToDatabase(); // clear house panel for online player var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); // wait for slumlord inventory biotas above to save actionChain.AddAction(onlinePlayer, onlinePlayer.HandleActionQueryHouse); actionChain.EnqueueChain(); }
/** * Test win conditions, depth, and external signals to check whether to cut off or not. * * Return true if terminal, else false. */ bool ApplyCutoff(ActionChain node, int depth) { if (node.Board.Winner != null) { node.score = node.Board.Winner == Controlling ? IS_WIN : IS_LOSS; return true; } else if (depth >= SOFT_PLIES) { // iterative deepening exception for dangerous positions // TODO neaten up the logic if (node.Board.Pieces.Values.Any(piece => piece.Occupies.Type == BoardSquareType.EDGE) && depth < HARD_PLIES && KeepSearching) { return false; } node.score = ScoreBoard (Controlling, node.Board); return true; } else if (KeepSearching == false) { // Disregard nodes that haven't yet been evaluated fully, fudge it // by returning an unfavourable score rather than eliminating it node.score = TurnTaker (depth) == Controlling ? IS_LOSS : IS_WIN; return true; } return false; }
/// <summary> /// Launches a missile attack from monster to target /// </summary> public void LaunchMissile() { //IsTurning = false; var weapon = GetEquippedMissileWeapon(); if (weapon == null || AttackTarget == null) { return; } var ammo = weapon.IsAmmoLauncher ? GetEquippedAmmo() : weapon; if (ammo == null) { return; } // ensure direct line of sight if (!IsDirectVisible(AttackTarget)) { NextAttackTime = Timers.RunningTime + 1.0f; return; } // should this be called each launch? AttackHeight = ChooseAttackHeight(); var dist = GetDistanceToTarget(); //Console.WriteLine("RangeAttack: " + dist); if (DebugMove) { Console.WriteLine($"[{Timers.RunningTime}] - {Name} ({Guid}) - LaunchMissile"); } // get z-angle for aim motion var aimVelocity = GetAimVelocity(AttackTarget); var aimLevel = GetAimLevel(aimVelocity); // calculate projectile spawn pos and velocity var localOrigin = GetProjectileSpawnOrigin(ammo.WeenieClassId, aimLevel); var velocity = CalculateProjectileVelocity(localOrigin, AttackTarget, out Vector3 origin, out Quaternion orientation); //Console.WriteLine($"Velocity: {velocity}"); // launch animation var actionChain = new ActionChain(); var launchTime = EnqueueMotion(actionChain, aimLevel); //Console.WriteLine("LaunchTime: " + launchTime); // launch projectile actionChain.AddAction(this, () => { if (IsDead) { return; } var sound = GetLaunchMissileSound(weapon); EnqueueBroadcast(new GameMessageSound(Guid, sound, 1.0f)); // TODO: monster stamina usage if (AttackTarget != null) { var projectile = LaunchProjectile(weapon, ammo, AttackTarget, origin, orientation, velocity); UpdateAmmoAfterLaunch(ammo); } }); // will ammo be depleted? /*if (ammo.StackSize == null || ammo.StackSize <= 1) * { * // compare monsters: lugianmontokrenegade / sclavusse / zombielichtowerarcher * actionChain.EnqueueChain(); * NextMoveTime = NextAttackTime = Timers.RunningTime + launchTime + MissileDelay; * return; * }*/ // reload animation var animSpeed = GetAnimSpeed(); var reloadTime = EnqueueMotion(actionChain, MotionCommand.Reload, animSpeed); //Console.WriteLine("ReloadTime: " + reloadTime); // reset for next projectile EnqueueMotion(actionChain, MotionCommand.Ready); var linkTime = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Reload, MotionCommand.Ready); if (weapon.IsThrownWeapon) { actionChain.EnqueueChain(); actionChain = new ActionChain(); actionChain.AddDelaySeconds(linkTime); } //Console.WriteLine($"Reload time: launchTime({launchTime}) + reloadTime({reloadTime}) + linkTime({linkTime})"); actionChain.AddAction(this, () => EnqueueBroadcast(new GameMessageParentEvent(this, ammo, ACE.Entity.Enum.ParentLocation.RightHand, ACE.Entity.Enum.Placement.RightHandCombat))); actionChain.EnqueueChain(); var timeOffset = launchTime + reloadTime + linkTime; var missileDelay = MissileDelay; if (!weapon.IsAmmoLauncher) { missileDelay *= 1.5f; } NextMoveTime = NextAttackTime = Timers.RunningTime + timeOffset + missileDelay; }
bool IsStupidMove(ActionChain chain) { ActionPair moveProposal = chain.actions.Last (); if (moveProposal.action == ActionTaken.SKIPPED) { return false; } Board board = chain.Board; Piece movedPiece = board.Pieces [board.Squares [moveProposal.toLoc.x, moveProposal.toLoc.y]]; if (movedPiece.Occupies.Adjacent .Any (square => square.Type == BoardSquareType.EDGE) && movedPiece.Push.CheckPushes ().Count == 0) { return true; } else { return false; } }
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 = WieldedObjects.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)); } }
/// <summary> /// The base level minmax function, which utilises a different return type and parellelises the search /// </summary> ActionChain MinMax0(ActionChain baseChain) { List<ActionChain> turnActions = PlayOneTurn (Controlling, baseChain.Board); NodesEvaluated += turnActions.Count; // Since we're unlikely to be able to evaluate all nodes on a cold pass, evaluate them in order // of immediate potential in order to discover the most fruitful nodes first turnActions.ForEach(node => node.score = ScoreBoard(Controlling, node.Board)); turnActions.Sort ((a,b) => { return a.score.CompareTo (b.score);} ); // execute in parallel for speed boost, shuffle to introduce nondeterminism between same scores int nodesBatch = 100; Parallel.ForEach (Partitioner.Create (0, turnActions.Count, nodesBatch), range => { for (int i = range.Item1; i < range.Item2; i++) { ScoringFn (0, turnActions [i]); } } ); turnActions.Shuffle (); turnActions.Sort ((a,b) => { return a.score.CompareTo (b.score);} ); return turnActions.Last (); }
/// <summary> /// Launches a missile attack from monster to target /// </summary> public void LaunchMissile() { //IsTurning = false; var weapon = GetEquippedMissileWeapon(); if (weapon == null || AttackTarget == null) { return; } var ammo = weapon.IsAmmoLauncher ? GetEquippedAmmo() : weapon; if (ammo == null) { return; } // ensure direct line of sight if (!IsDirectVisible(AttackTarget)) { NextAttackTime = DateTime.UtcNow.AddSeconds(1.0f); return; } // should this be called each launch? AttackHeight = ChooseAttackHeight(); var dist = GetDistanceToTarget(); //Console.WriteLine("RangeAttack: " + dist); // launch animation var actionChain = new ActionChain(); var launchTime = EnqueueMotion(actionChain, MotionCommand.AimLevel); //Console.WriteLine("LaunchTime: " + launchTime); // launch projectile float targetTime = 0.0f; actionChain.AddAction(this, () => { var sound = GetLaunchMissileSound(weapon); EnqueueBroadcast(new GameMessageSound(Guid, sound, 1.0f)); // TODO: monster stamina usage var projectile = LaunchProjectile(ammo, AttackTarget, out targetTime); UpdateAmmoAfterLaunch(ammo); }); // will ammo be depleted? if (ammo.StackSize == 1) { actionChain.EnqueueChain(); NextAttackTime = DateTime.UtcNow.AddSeconds(launchTime + MissileDelay); return; } // reload animation var reloadTime = EnqueueMotion(actionChain, MotionCommand.Reload); //Console.WriteLine("ReloadTime: " + reloadTime); // reset for next projectile EnqueueMotion(actionChain, MotionCommand.Ready); var linkTime = MotionTable.GetAnimationLength(MotionTableId, CurrentMotionState.Stance, MotionCommand.Reload, MotionCommand.Ready); //Console.WriteLine("LinkTime: " + linkTime); actionChain.AddAction(this, () => EnqueueBroadcast(new GameMessageParentEvent(this, ammo, (int)ACE.Entity.Enum.ParentLocation.RightHand, (int)ACE.Entity.Enum.Placement.RightHandCombat))); actionChain.EnqueueChain(); var timeOffset = launchTime + reloadTime + linkTime; NextAttackTime = DateTime.UtcNow.AddSeconds(timeOffset + MissileDelay); }
void StoreBestAction(ActionChain best) { Instructions.Clear (); for (int i = 0; i < 3 && i < best.actions.Count; i++) { ActionPair pair = best.actions [i]; if (pair.action == ActionTaken.SKIPPED) { DebugFn ("AI SKIP " + Master.Turn.Phase ()); Instructions.Add (() => { Master.Turn.Control ().Skip ();}); } else if (pair.action == ActionTaken.MOVED) { DebugFn ("AI MOVE " + pair.fromLoc.ToString () + " " + pair.toLoc.ToString ()); Instructions.Add (() => { Master.Turn.Control ().Move (pair.fromLoc, pair.toLoc);}); } else if (pair.action == ActionTaken.PUSHED) { DebugFn ("AI PUSH " + pair.fromLoc.ToString () + " " + pair.toLoc.ToString ()); Instructions.Add (() => { Master.Turn.Control ().Push (pair.fromLoc, pair.toLoc); }); } else { throw new Exception ("Unknown AI action"); } } }
public static void UseUnlocker(Player player, WorldObject unlocker, WorldObject target) { ActionChain chain = new ActionChain(); chain.AddAction(player, () => { if (unlocker.WeenieType == WeenieType.Lockpick && player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Trained && player.Skills[Skill.Lockpick].AdvancementClass != SkillAdvancementClass.Specialized) { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouArentTrainedInLockpicking)); return; } if (target is Lock @lock) { UnlockResults result = UnlockResults.IncorrectKey; if (unlocker.WeenieType == WeenieType.Lockpick) { result = @lock.Unlock(player.Skills[Skill.Lockpick].Current); } else if (unlocker is Key woKey) { if (target is Door woDoor) { if (woDoor.LockCode == "") // the door isn't to be opened with keys { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat)); return; } } result = @lock.Unlock(woKey.KeyCode); } switch (result) { case UnlockResults.UnlockSuccess: if (unlocker.WeenieType == WeenieType.Lockpick) { player.HandleActionApplySoundEffect(Sound.Lockpicking); // Sound.Lockpicking doesn't work via EnqueueBroadcastSound for some reason. } player.Session.Network.EnqueueSend(new GameMessageSystemChat($"You have successfully picked the lock! It is now unlocked.", ChatMessageType.Craft)); ConsumeUnlocker(player, unlocker); break; case UnlockResults.Open: player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockWhatIsOpen)); break; case UnlockResults.AlreadyUnlocked: player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.LockAlreadyUnlocked)); break; case UnlockResults.PickLockFailed: target.CurrentLandblock?.EnqueueBroadcastSound(target, Sound.PicklockFail); ConsumeUnlocker(player, unlocker); break; case UnlockResults.CannotBePicked: player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat)); break; case UnlockResults.IncorrectKey: player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.KeyDoesntFitThisLock)); break; } } else { player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session, WeenieError.YouCannotLockOrUnlockThat)); } }); chain.EnqueueChain(); }
public void Link(ActionChain previous) { previous.ClearBoard (); List<ActionPair> newAction = new List<ActionPair> (); newAction.AddRange (previous.actions); newAction.AddRange (actions); actions = newAction; }
/// <summary> /// This is raised by Player.HandleActionUseItem.<para /> /// The item should be in the players possession. /// </summary> public override void UseItem(Player player) { bool success = true; string failReason = "You are unable to read the scroll."; switch (Power) { // research: http://asheron.wikia.com/wiki/Announcements_-_2002/06_-_Castling case spellLevel2: // Level 2 case spellLevel3: // Level 3 case spellLevel4: // Level 4 case spellLevel5: // Level 5 case spellLevel6: // Level 6 if (!player.CanReadScroll(School, Power)) { success = false; failReason = "You are not skilled enough in the inscribed spell's school of magic to understand the writing on this scroll."; } break; } if (player.SpellIsKnown(SpellId)) { success = false; failReason = "You already know the spell inscribed upon this scroll."; } var actionChain = new ActionChain(); actionChain .AddAction(player, () => player.EnqueueBroadcastMotion(motionReading)) .AddDelaySeconds(2); if (success) { actionChain.AddAction(player, () => { player.LearnSpellWithNetworking(SpellId); player.EnqueueBroadcastMotion(motionReady); if (player.TryRemoveFromInventoryWithNetworking(this)) { player.Session.Network.EnqueueSend(new GameMessageSystemChat("The scroll is destroyed.", ChatMessageType.Magic)); Destroy(); } }); } else { actionChain .AddDelaySeconds(2) .AddAction(player, () => { player.EnqueueBroadcastMotion(motionReady); player.Session.Network.EnqueueSend(new GameMessageSystemChat($"{failReason}", ChatMessageType.Magic)); }); } actionChain .AddAction(player, () => player.Session.Network.EnqueueSend(new GameEventUseDone(player.Session))); actionChain.EnqueueChain(); }