/// <summary> /// 指定したモーションの再生を開始する。 /// </summary> /// <param name="motion">再生するモーション</param> /// <param name="priority">モーションの優先度。高いほど優先される。</param> /// <param name="loop_enabled">trueのときループが有効なモーションではループを有効にする</param> /// <returns>再生が開始された場合はCubismMotionQueueEntryを返す</returns> public CubismMotionQueueEntry StartMotion(ICubismMotion motion, bool loop_enabled = false) { var queue_entry = new CubismMotionQueueEntry(); queue_entry.Motion = motion; queue_entry.LoopEnabled = motion.CanLoop && loop_enabled; MotionQueue.Add(queue_entry); return(queue_entry); }
/// <summary> /// モーションを更新して、モデルにパラメータ値を反映する。 /// </summary> /// <param name="elapsed_time">経過時間[秒]</param> public void Update(double elapsed_time) { foreach (var queue_entry in MotionQueue) { // モーションを計算する string[] event_data; if (queue_entry.Update(elapsed_time, out event_data) == false) { continue; } // イベントが発生していればコールバックを呼ぶ if (EventCallbak != null) { foreach (string event_value in event_data) { EventCallbak(this, event_value, EventParameter); } } } // 終了したモーションをキューから除く MotionQueue.RemoveAll(x => x.Finished); }
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($"{WorldObject.Name}.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, () => { //Console.WriteLine($"{sourceObject.Name} running starting motion {(MotionStance)emote.Style}, {(MotionCommand)emote.Substyle}"); sourceObject.ExecuteMotion(startingMotion); }); } } else { if (sourceObject.CurrentMotionState.Commands.Count > 0 && sourceObject.CurrentMotionState.Commands[0].Motion == startingMotion.Commands[0].Motion) { actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { //Console.WriteLine($"{sourceObject.Name} running motion {(MotionStance)emote.Style}, {(MotionCommand)emoteAction.Motion}"); float?maxRange = ClientMaxAnimRange; if (MotionQueue.Contains((MotionCommand)emoteAction.Motion)) { maxRange = null; } sourceObject.ExecuteMotion(motion, true, maxRange); }); 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, () => { //Console.WriteLine($"{sourceObject.Name} running starting motion again {(MotionStance)emote.Style}, {(MotionCommand)emote.Substyle}"); sourceObject.ExecuteMotion(startingMotion); }); } } } } else { var motion = new UniversalMotion(MotionStance.NonCombat, new MotionItem((MotionCommand)emoteAction.Motion, emoteAction.Extent)); actionChain.AddDelaySeconds(emoteAction.Delay); actionChain.AddAction(sourceObject, () => { //Console.WriteLine($"{sourceObject.Name} running motion (block 2) {(MotionStance)emote.Style}, {(MotionCommand)emoteAction.Motion}"); sourceObject.ExecuteMotion(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; } }