/// <summary> /// Attempts to use an item - checks activation requirements /// </summary> public void TryUseItem(WorldObject item, bool success = true) { //Console.WriteLine($"{Name}.TryUseItem({item.Name}, {success})"); LastUseTime = 0.0f; if (success) { item.OnActivate(this); } // manually managed if (LastUseTime == float.MinValue) { return; } var actionChain = new ActionChain(); actionChain.AddDelaySeconds(LastUseTime); actionChain.AddAction(this, () => SendUseDoneEvent()); actionChain.EnqueueChain(); NextUseTime = DateTime.UtcNow + TimeSpan.FromSeconds(LastUseTime); }
public void TrySwitchToMeleeAttack() { // 24139 - Invisible Assailant never switches to melee? if (Visibility) { return; } SwitchWeaponsPending = true; var nextSwitchTime = NextMoveTime - MissileDelay; if (nextSwitchTime > Timers.RunningTime) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(nextSwitchTime - Timers.RunningTime); actionChain.AddAction(this, () => SwitchToMeleeAttack()); actionChain.EnqueueChain(); } else { SwitchToMeleeAttack(); } }
/// <summary> /// Handles updating the vitae penalty through earned XP /// </summary> /// <param name="amount">The amount of XP to apply to the vitae penalty</param> private void UpdateXpVitae(long amount) { var vitaePenalty = EnchantmentManager.GetVitae().StatModValue; var startPenalty = vitaePenalty; var maxPool = (int)VitaeCPPoolThreshold(vitaePenalty, DeathLevel.Value); var curPool = VitaeCpPool + amount; while (curPool >= maxPool) { curPool -= maxPool; vitaePenalty = EnchantmentManager.ReduceVitae(); if (vitaePenalty == 1.0f) { break; } maxPool = (int)VitaeCPPoolThreshold(vitaePenalty, DeathLevel.Value); } VitaeCpPool = (int)curPool; Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.VitaeCpPool, VitaeCpPool.Value)); if (vitaePenalty != startPenalty) { Session.Network.EnqueueSend(new GameMessageSystemChat("Your experience has reduced your Vitae penalty!", ChatMessageType.Magic)); EnchantmentManager.SendUpdateVitae(); } if (vitaePenalty == 1.0f) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(2.0f); actionChain.AddAction(this, () => EnchantmentManager.RemoveVitae()); actionChain.EnqueueChain(); } }
/// <summary> /// Do the player log out work.<para /> /// If you want to force a player to logout, use Session.LogOffPlayer(). /// </summary> public bool LogOut(bool clientSessionTerminatedAbruptly = false, bool forceImmediate = false) { if (PKLogoutActive && !forceImmediate) { Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouHaveBeenInPKBattleTooRecently)); Session.Network.EnqueueSend(new GameMessageSystemChat("Logging out in 20s...", ChatMessageType.Magic)); PKLogout = true; var actionChain = new ActionChain(); actionChain.AddDelaySeconds(20.0f); actionChain.AddAction(this, () => { LogOut_Inner(clientSessionTerminatedAbruptly); Session.logOffRequestTime = DateTime.UtcNow; }); actionChain.EnqueueChain(); return(false); } LogOut_Inner(clientSessionTerminatedAbruptly); return(true); }
/// <summary> /// Called when the player enters portal space after dying /// </summary> public void TeleportOnDeath() { // teleport to sanctuary or best location var newPosition = Sanctuary ?? Instantiation ?? Location; Teleport(newPosition); // Stand back up SetCombatMode(CombatMode.NonCombat); var teleportChain = new ActionChain(); teleportChain.AddDelaySeconds(3.0f); teleportChain.AddAction(this, () => { // currently happens while in portal space var newHealth = (uint)Math.Round(Health.MaxValue * 0.75f); var newStamina = (uint)Math.Round(Stamina.MaxValue * 0.75f); var newMana = (uint)Math.Round(Mana.MaxValue * 0.75f); var msgHealthUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, newHealth); var msgStaminaUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Stamina, newStamina); var msgManaUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Mana, newMana); UpdateVital(Health, newHealth); UpdateVital(Stamina, newStamina); UpdateVital(Mana, newMana); Session.Network.EnqueueSend(msgHealthUpdate, msgStaminaUpdate, msgManaUpdate); // reset damage history for this player DamageHistory.Reset(); }); teleportChain.EnqueueChain(); }
private void HandleManaDepleted(WorldObject item) { 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); // unsure if these messages / sounds were ever sent in retail, // or if it just purged the enchantments invisibly // doing a delay here to prevent 'SpellExpired' sounds from overlapping with 'ItemManaDepleted' var actionChain = new ActionChain(); actionChain.AddDelaySeconds(2.0f); actionChain.AddAction(this, () => { foreach (var spellId in item.Biota.GetKnownSpellsIds(item.BiotaDatabaseLock)) { RemoveItemSpell(item, (uint)spellId); } }); actionChain.EnqueueChain(); item.OnSpellsDeactivated(); }
/// <summary> /// Performs a melee attack for the monster /// </summary> public void MeleeAttack() { NextAttackTime = Timer.CurrentTime + MeleeDelay; var player = AttackTarget as Player; if (player.Health.Current <= 0) { return; } // select random body part @ current attack height var bodyPart = GetBodyPart(); DoSwingMotion(AttackTarget, out float animLength); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(animLength / 2.0f); actionChain.AddAction(this, () => { var critical = false; var damageType = DamageType.Undef; var damage = CalculateDamage(ref damageType, bodyPart, ref critical); if (damage > 0.0f) { player.TakeDamage(this, damageType, damage, bodyPart, critical); } else { player.Session.Network.EnqueueSend(new GameMessageSystemChat($"You evaded {Name}!", ChatMessageType.CombatEnemy)); } }); actionChain.EnqueueChain(); }
public static void HandleWeapons(Session session, params string[] parameters) { HashSet <uint> weaponsTest = new HashSet <uint>() { 93, 127, 130, 136, 136, 136, 148, 300, 307, 311, 326, 338, 348, 350, 7765, 12748, 12463, 31812 }; ActionChain chain = new ActionChain(); chain.AddAction(session.Player, () => { foreach (uint weenieId in weaponsTest) { WorldObject loot = LootGenerationFactory.CreateTestWorldObject(session.Player, weenieId); loot.ContainerId = session.Player.Guid.Full; session.Player.AddToInventory(loot); session.Player.TrackObject(loot); session.Player.UpdatePlayerBurden(); session.Network.EnqueueSend( new GameMessagePutObjectInContainer(session, session.Player.Guid, loot, 0), new GameMessageUpdateInstanceId(loot.Guid, session.Player.Guid, PropertyInstanceId.Container)); } }); chain.EnqueueChain(); }
/// <summary> /// This is raised by Player.HandleActionUseItem.<para /> /// The item should be in the players possession. /// </summary> public override void ActOnUse(WorldObject activator) { // Research: http://asheron.wikia.com/wiki/Announcements_-_2002/06_-_Castling if (!(activator is Player player)) { return; } if (Spell == null) { Console.WriteLine($"{Name}.ActOnUse({activator.Name}) - SpellDID not found for {WeenieClassId}"); return; } if (player.IsBusy || player.Teleporting || player.suicideInProgress) { player.SendWeenieError(WeenieError.YoureTooBusy); return; } player.IsBusy = true; var actionChain = new ActionChain(); if (player.CombatMode != CombatMode.NonCombat) { var stanceTime = player.SetCombatMode(CombatMode.NonCombat); actionChain.AddDelaySeconds(stanceTime); player.LastUseTime += stanceTime; } var animTime = player.EnqueueMotion(actionChain, MotionCommand.Reading); player.LastUseTime += animTime; var readTime = 1.0f; actionChain.AddDelaySeconds(readTime); player.LastUseTime += readTime; actionChain.AddAction(player, () => { if (player.SpellIsKnown(Spell.Id)) { // verify unknown spell player.Session.Network.EnqueueSend(new GameMessageSystemChat("You already know that spell!", ChatMessageType.Broadcast)); return; } var skill = Spell.GetMagicSkill(); var playerSkill = player.GetCreatureSkill(skill); if (!player.CanReadScroll(this)) { var msg = ""; if (playerSkill.AdvancementClass < SkillAdvancementClass.Trained) { msg = $"You are not trained in {playerSkill.Skill.ToSentence()}!"; } else { msg = $"You are not skilled enough in {playerSkill.Skill.ToSentence()} to learn this spell."; } player.Session.Network.EnqueueSend(new GameMessageSystemChat(msg, ChatMessageType.Broadcast)); return; } if (player.TryConsumeFromInventoryWithNetworking(this)) { player.LearnSpellWithNetworking(Spell.Id); player.Session.Network.EnqueueSend(new GameMessageSystemChat("The scroll is destroyed.", ChatMessageType.Broadcast)); } }); // FIXME: return stance time player.EnqueueMotion(actionChain, MotionCommand.Ready); player.LastUseTime += animTime; // return stance actionChain.AddDelaySeconds(animTime); actionChain.AddAction(player, () => player.IsBusy = false); actionChain.EnqueueChain(); }
public void HandleSwitchToMissileCombatMode(ActionChain combatModeChain) { // TODO and FIXME: GetInventoryItem doesn't work for this so this function is effectively broke HeldItem mEquipedMissile = Children.Find(s => s.EquipMask == EquipMask.MissileWeapon); if (mEquipedMissile?.Guid != null) { WorldObject missileWeapon = GetWieldedItem(new ObjectGuid(mEquipedMissile.Guid)); if (missileWeapon == null) { log.InfoFormat("Changing combat mode for {0} - could not locate wielded weapon {1}", Guid, mEquipedMissile.Guid); return; } var mEquipedAmmo = GetEquippedAmmo(); MotionStance ms; CombatStyle cs; if (missileWeapon.DefaultCombatStyle != null) { cs = missileWeapon.DefaultCombatStyle.Value; } else { log.InfoFormat("Changing combat mode for {0} - wielded item {1} has not be assigned a default combat style", Guid, mEquipedMissile.Guid); return; } switch (cs) { case CombatStyle.Bow: ms = MotionStance.BowAttack; break; case CombatStyle.Crossbow: ms = MotionStance.CrossBowAttack; break; default: ms = MotionStance.Invalid; break; } UniversalMotion mm = new UniversalMotion(ms); mm.MovementData.CurrentStyle = (ushort)ms; CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this)); SetMotionState(this, mm); if (mEquipedAmmo != null) { mm.MovementData.ForwardCommand = (uint)MotionCommand.Reload; SetMotionState(this, mm); CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageUpdatePosition(this)); // FIXME: (Og II)<this is a hack for now to be removed. Need to pull delay from dat file combatModeChain.AddDelaySeconds(0.25); mm.MovementData.ForwardCommand = (ushort)MotionCommand.Invalid; SetMotionState(this, mm); // FIXME: (Og II)<this is a hack for now to be removed. Need to pull delay from dat file combatModeChain.AddDelaySeconds(0.40); // add to player tracking var wielder = CurrentLandblock.GetObject(new ObjectGuid(mEquipedAmmo.WielderId.Value)); combatModeChain.AddAction(this, () => CurrentLandblock.EnqueueActionBroadcast(Location, Landblock.MaxObjectRange, (Player p) => p.TrackObject(wielder))); combatModeChain.AddAction(this, () => CurrentLandblock.EnqueueBroadcast(Location, Landblock.MaxObjectRange, new GameMessageParentEvent(this, mEquipedAmmo, 1, 1))); } var player = this as Player; if (player != null) { player.Session.Network.EnqueueSend(new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.CombatMode, (int)CombatMode.Missile)); } } }
private static void DoPlayerEnterWorld(Session session, Character character, Biota playerBiota, PossessedBiotas possessedBiotas) { Player player; Player.HandleNoLogLandblock(playerBiota, out var playerLoggedInOnNoLogLandblock); var stripAdminProperties = false; var addAdminProperties = false; var addSentinelProperties = false; if (ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) { if (session.AccessLevel <= AccessLevel.Advocate) // check for elevated characters { if (playerBiota.WeenieType == WeenieType.Admin || playerBiota.WeenieType == WeenieType.Sentinel) // Downgrade weenie { character.IsPlussed = false; playerBiota.WeenieType = WeenieType.Creature; stripAdminProperties = true; } } else if (session.AccessLevel >= AccessLevel.Sentinel && session.AccessLevel <= AccessLevel.Envoy) { if (playerBiota.WeenieType == WeenieType.Creature || playerBiota.WeenieType == WeenieType.Admin) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = WeenieType.Sentinel; addSentinelProperties = true; } } else // Developers and Admins { if (playerBiota.WeenieType == WeenieType.Creature || playerBiota.WeenieType == WeenieType.Sentinel) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = WeenieType.Admin; addAdminProperties = true; } } } if (playerBiota.WeenieType == WeenieType.Admin) { player = new Admin(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else if (playerBiota.WeenieType == WeenieType.Sentinel) { player = new Sentinel(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else { player = new Player(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } session.SetPlayer(player); if (stripAdminProperties) // continue stripping properties { player.CloakStatus = CloakStatus.Undef; player.Attackable = true; player.SetProperty(PropertyBool.DamagedByCollisions, true); player.AdvocateLevel = null; player.ChannelsActive = null; player.ChannelsAllowed = null; player.Invincible = false; player.Cloaked = null; player.IgnoreHouseBarriers = false; player.IgnorePortalRestrictions = false; player.SafeSpellComponents = false; player.ReportCollisions = true; player.ChangesDetected = true; player.CharacterChangesDetected = true; } if (addSentinelProperties || addAdminProperties) // continue restoring properties to default { WorldObject weenie; if (addAdminProperties) { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("admin"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)); } else { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("sentinel"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)); } if (weenie != null) { player.CloakStatus = CloakStatus.Off; player.Attackable = weenie.Attackable; player.SetProperty(PropertyBool.DamagedByCollisions, false); player.AdvocateLevel = weenie.GetProperty(PropertyInt.AdvocateLevel); player.ChannelsActive = (Channel?)weenie.GetProperty(PropertyInt.ChannelsActive); player.ChannelsAllowed = (Channel?)weenie.GetProperty(PropertyInt.ChannelsAllowed); player.Invincible = false; player.Cloaked = false; player.ChangesDetected = true; player.CharacterChangesDetected = true; } } // If the client is missing a location, we start them off in the starter town they chose if (session.Player.Location == null) { if (session.Player.Instantiation != null) { session.Player.Location = new Position(session.Player.Instantiation); } else { session.Player.Location = new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); // ultimate fallback } } var olthoiPlayerReturnedToLifestone = session.Player.IsOlthoiPlayer && character.TotalLogins >= 1 && session.Player.LoginAtLifestone; if (olthoiPlayerReturnedToLifestone) { session.Player.Location = new Position(session.Player.Sanctuary); } session.Player.PlayerEnterWorld(); var success = LandblockManager.AddObject(session.Player, true); if (!success) { // send to lifestone, or fallback location var fixLoc = session.Player.Sanctuary ?? new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); log.Error($"WorldManager.DoPlayerEnterWorld: failed to spawn {session.Player.Name}, relocating to {fixLoc.ToLOCString()}"); session.Player.Location = new Position(fixLoc); LandblockManager.AddObject(session.Player, true); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(5.0f); actionChain.AddAction(session.Player, () => { if (session != null && session.Player != null) { session.Player.Teleport(fixLoc); } }); actionChain.EnqueueChain(); } // These warnings are set by DDD_InterrogationResponse if ((session.DatWarnCell || session.DatWarnLanguage || session.DatWarnPortal) && PropertyManager.GetBool("show_dat_warning").Item) { var msg = PropertyManager.GetString("dat_warning_msg").Item; var chatMsg = new GameMessageSystemChat(msg, ChatMessageType.System); session.Network.EnqueueSend(chatMsg); } var popup_header = PropertyManager.GetString("popup_header").Item; var popup_motd = PropertyManager.GetString("popup_motd").Item; var popup_welcome = player.IsOlthoiPlayer ? PropertyManager.GetString("popup_welcome_olthoi").Item : PropertyManager.GetString("popup_welcome").Item; if (character.TotalLogins <= 1) { if (player.IsOlthoiPlayer) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_welcome, popup_motd))); } else { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd, popup_welcome))); } } else if (!string.IsNullOrEmpty(popup_motd)) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd))); } var info = "Welcome to Asheron's Call\n powered by ACEmulator\n\nFor more information on commands supported by this server, type @acehelp\n"; session.Network.EnqueueSend(new GameMessageSystemChat(info, ChatMessageType.Broadcast)); var server_motd = PropertyManager.GetString("server_motd").Item; if (!string.IsNullOrEmpty(server_motd)) { session.Network.EnqueueSend(new GameMessageSystemChat($"{server_motd}\n", ChatMessageType.Broadcast)); } if (olthoiPlayerReturnedToLifestone) { session.Network.EnqueueSend(new GameMessageSystemChat("You have returned to the Olthoi Queen to serve the hive.", ChatMessageType.Broadcast)); } else if (playerLoggedInOnNoLogLandblock) // see http://acpedia.org/wiki/Mount_Elyrii_Hive { session.Network.EnqueueSend(new GameMessageSystemChat("The currents of portal space cannot return you from whence you came. Your previous location forbids login.", 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($"{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; } }
/// <summary> /// Performs a melee attack for the monster /// </summary> /// <returns>The length in seconds for the attack animation</returns> public float MeleeAttack() { var target = AttackTarget as Creature; var targetPlayer = AttackTarget as Player; var targetPet = AttackTarget as CombatPet; var combatPet = this as CombatPet; if (target == null || !target.IsAlive) { Sleep(); return(0.0f); } // choose a random combat maneuver var maneuver = GetCombatManeuver(); if (maneuver == null) { Console.WriteLine($"Combat maneuver null! Stance {CurrentMotionState.Stance}, MotionTable {MotionTableId:X8}"); return(0.0f); } AttackHeight = maneuver.AttackHeight; // select random body part @ current attack height var bodyPart = BodyParts.GetBodyPart(AttackHeight.Value); DoSwingMotion(AttackTarget, maneuver, out float animLength, out var attackFrames); PhysicsObj.stick_to_object(AttackTarget.PhysicsObj.ID); var numStrikes = attackFrames.Count; var actionChain = new ActionChain(); var prevTime = 0.0f; for (var i = 0; i < numStrikes; i++) { actionChain.AddDelaySeconds(attackFrames[i] * animLength - prevTime); prevTime = attackFrames[i] * animLength; actionChain.AddAction(this, () => { if (AttackTarget == null) { return; } var critical = false; var damageType = DamageType.Undef; var shieldMod = 1.0f; var damage = CalculateDamage(ref damageType, maneuver, bodyPart, ref critical, ref shieldMod); if (damage != null) { if (combatPet != null || targetPet != null) { // combat pet inflicting or receiving damage //Console.WriteLine($"{target.Name} taking {Math.Round(damage)} {damageType} damage from {Name}"); target.TakeDamage(this, damageType, damage.Value); EmitSplatter(target, damage.Value); } else { // this is a player taking damage targetPlayer.TakeDamage(this, damageType, damage.Value, bodyPart, critical); if (shieldMod != 1.0f) { var shieldSkill = targetPlayer.GetCreatureSkill(Skill.Shield); Proficiency.OnSuccessUse(targetPlayer, shieldSkill, shieldSkill.Current); // ? } } } else { target.OnEvade(this, CombatType.Melee); } }); } actionChain.EnqueueChain(); // TODO: figure out exact speed / delay formula var meleeDelay = ThreadSafeRandom.Next(MeleeDelayMin, MeleeDelayMax); NextAttackTime = Timers.RunningTime + animLength + meleeDelay; return(animLength); }
public void PlayerEnterWorld() { PlayerManager.SwitchPlayerFromOfflineToOnline(this); // Save the the LoginTimestamp var lastLoginTimestamp = Time.GetUnixTime(); SetProperty(PropertyInt.LoginTimestamp, (int)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; } // SendSelf will trigger the entrance into portal space SendSelf(); // Init the client with the chat channel ID's, and then notify the player that they've choined the associated channels. UpdateChatChannels(); var general = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "General"); var trade = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "Trade"); var lfg = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "LFG"); var roleplay = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "Roleplay"); Session.Network.EnqueueSend(general, trade, lfg, roleplay); // 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(); if (PlayerKillerStatus == PlayerKillerStatus.PKLite && !PropertyManager.GetBool("pkl_server").Item) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); actionChain.AddAction(this, () => { UpdateProperty(this, PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK, true); Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNonPKAgain)); }); actionChain.EnqueueChain(); } HandleDBUpdates(); }
/// <summary> /// Performs a melee attack for the monster /// </summary> /// <returns>The length in seconds for the attack animation</returns> public float MeleeAttack() { var target = AttackTarget as Creature; var targetPlayer = AttackTarget as Player; var targetPet = AttackTarget as CombatPet; var combatPet = this as CombatPet; if (target == null || !target.IsAlive) { FindNextTarget(); return(0.0f); } if (CurrentMotionState.Stance == MotionStance.NonCombat) { DoAttackStance(); } // choose a random combat maneuver var maneuver = GetCombatManeuver(); if (maneuver == null) { Console.WriteLine($"Combat maneuver null! Stance {CurrentMotionState.Stance}, MotionTable {MotionTableId:X8}"); return(0.0f); } AttackHeight = maneuver.AttackHeight; DoSwingMotion(AttackTarget, maneuver, out float animLength, out var attackFrames); PhysicsObj.stick_to_object(AttackTarget.PhysicsObj.ID); var numStrikes = attackFrames.Count; var actionChain = new ActionChain(); var prevTime = 0.0f; for (var i = 0; i < numStrikes; i++) { actionChain.AddDelaySeconds(attackFrames[i] * animLength - prevTime); prevTime = attackFrames[i] * animLength; actionChain.AddAction(this, () => { if (AttackTarget == null || IsDead) { return; } if (WeenieType == WeenieType.GamePiece) { target.TakeDamage(this, DamageType.Slash, target.Health.Current); (this as GamePiece).OnDealtDamage(); return; } var weapon = GetEquippedWeapon(); var damageEvent = DamageEvent.CalculateDamage(this, target, weapon, maneuver); //var damage = CalculateDamage(ref damageType, maneuver, bodyPart, ref critical, ref shieldMod); if (damageEvent.HasDamage) { if (combatPet != null || targetPet != null) { // combat pet inflicting or receiving damage //Console.WriteLine($"{target.Name} taking {Math.Round(damage)} {damageType} damage from {Name}"); target.TakeDamage(this, damageEvent.DamageType, damageEvent.Damage); EmitSplatter(target, damageEvent.Damage); } else if (targetPlayer != null) { // this is a player taking damage targetPlayer.TakeDamage(this, damageEvent); if (damageEvent.ShieldMod != 1.0f) { var shieldSkill = targetPlayer.GetCreatureSkill(Skill.Shield); Proficiency.OnSuccessUse(targetPlayer, shieldSkill, shieldSkill.Current); // ? } } } else { target.OnEvade(this, CombatType.Melee); } if (combatPet != null) { combatPet.PetOnAttackMonster(target); } }); } actionChain.EnqueueChain(); // TODO: figure out exact speed / delay formula var meleeDelay = ThreadSafeRandom.Next(MeleeDelayMin, MeleeDelayMax); NextAttackTime = Timers.RunningTime + animLength + meleeDelay; return(animLength); }
private static void HandleCreateItemRecipe(Player player, WorldObject source, WorldObject target, Recipe recipe) { 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)); float craftAnimationLength = MotionTable.GetAnimationLength((uint)player.MotionTableId, MotionCommand.ClapHands); craftChain.AddDelaySeconds(craftAnimationLength); // craftChain.AddDelaySeconds(0.5); craftChain.AddAction(player, () => { if (recipe.SkillId != null && recipe.SkillDifficulty != null) { // there's a skill associated with this Skill skillId = (Skill)recipe.SkillId.Value; // this shouldn't happen, but sanity check for unexpected nulls if (!player.Skills.ContainsKey(skillId)) { log.Warn("Unexpectedly missing skill in Recipe usage"); player.SendUseDoneEvent(); return; } skill = player.Skills[skillId]; percentSuccess = skill.GetPercentSuccess(recipe.SkillDifficulty.Value); } // straight skill check, if applciable if (skill != null) { skillSuccess = _random.NextDouble() < percentSuccess; } if ((recipe.ResultFlags & (uint)RecipeResult.SourceItemDestroyed) > 0) { player.DestroyInventoryItem(source); } if ((recipe.ResultFlags & (uint)RecipeResult.TargetItemDestroyed) > 0) { player.DestroyInventoryItem(target); } if ((recipe.ResultFlags & (uint)RecipeResult.SourceItemUsesDecrement) > 0) { if (source.Structure <= 1) { player.DestroyInventoryItem(source); } else { source.Structure--; source.SendPartialUpdates(player.Session, _updateStructure); } } if ((recipe.ResultFlags & (uint)RecipeResult.TargetItemUsesDecrement) > 0) { if (target.Structure <= 1) { player.DestroyInventoryItem(target); } else { target.Structure--; target.SendPartialUpdates(player.Session, _updateStructure); } } if (skillSuccess) { WorldObject newObject1 = null; WorldObject newObject2 = null; if ((recipe.ResultFlags & (uint)RecipeResult.SuccessItem1) > 0 && recipe.SuccessItem1Wcid != null) { newObject1 = player.AddNewItemToInventory(recipe.SuccessItem1Wcid.Value); } if ((recipe.ResultFlags & (uint)RecipeResult.SuccessItem2) > 0 && recipe.SuccessItem2Wcid != null) { newObject2 = player.AddNewItemToInventory(recipe.SuccessItem2Wcid.Value); } var text = string.Format(recipe.SuccessMessage, source.Name, target.Name, newObject1?.Name, newObject2?.Name); var message = new GameMessageSystemChat(text, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } else { WorldObject newObject1 = null; WorldObject newObject2 = null; if ((recipe.ResultFlags & (uint)RecipeResult.FailureItem1) > 0 && recipe.FailureItem1Wcid != null) { newObject1 = player.AddNewItemToInventory(recipe.FailureItem1Wcid.Value); } if ((recipe.ResultFlags & (uint)RecipeResult.FailureItem2) > 0 && recipe.FailureItem2Wcid != null) { newObject2 = player.AddNewItemToInventory(recipe.FailureItem2Wcid.Value); } var text = string.Format(recipe.FailMessage, source.Name, target.Name, newObject1?.Name, newObject2?.Name); var message = new GameMessageSystemChat(text, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); } player.SendUseDoneEvent(); }); craftChain.EnqueueChain(); }
public void HandleActionUseItem(ObjectGuid usedItemId) { StopExistingMoveToChains(); var actionChain = new ActionChain(); actionChain.AddAction(this, () => { // Search our inventory first var item = GetInventoryItem(usedItemId); if (item != null) { item.UseItem(this, actionChain); } else { // Search the world second item = CurrentLandblock?.GetObject(usedItemId); if (item == null) { Session.Network.EnqueueSend(new GameEventUseDone(Session)); // todo add an argument that indicates the item was not found return; } if (item is Container) { lastUsedContainerId = usedItemId; } if (!IsWithinUseRadiusOf(item)) { var moveToChain = CreateMoveToChain(item, item.UseRadiusSquared, out var thisMoveToChainNumber); actionChain.AddChain(moveToChain); actionChain.AddDelaySeconds(0.50); // Make sure that after we've executed our MoveToChain, and waited our delay, we're still within use radius. actionChain.AddAction(this, () => { if (IsWithinUseRadiusOf(item) && thisMoveToChainNumber == moveToChainCounter) { actionChain.AddAction(item, () => item.ActOnUse(this)); } else { // Action is cancelled Session.Network.EnqueueSend(new GameEventUseDone(Session)); } }); } else { actionChain.AddAction(item, () => item.ActOnUse(this)); } } }); actionChain.EnqueueChain(); }
public override void ActOnUse(WorldObject activator) { if (!(activator is Player player)) { return; } if (IsBusy) { player.Session.Network.EnqueueSend(new GameEventWeenieErrorWithString(player.Session, WeenieErrorWithString.The_IsCurrentlyInUse, Name)); return; } if (player.PkLevel == PKLevel.PK && IsNPKSwitch && (Time.GetUnixTime() - player.PkTimestamp) < MinimumTimeSincePk) { IsBusy = true; player.IsBusy = true; var actionChain = new ActionChain(); if (UseTargetFailureAnimation != MotionCommand.Invalid) { var useMotion = UseTargetFailureAnimation; EnqueueBroadcastMotion(new Motion(this, useMotion)); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId); var useTime = motionTable.GetAnimationLength(useMotion); player.LastUseTime += useTime; actionChain.AddDelaySeconds(useTime); } actionChain.AddAction(player, () => { player.Session.Network.EnqueueSend(new GameEventWeenieError(player.Session, WeenieError.YouFeelAHarshDissonance)); player.IsBusy = false; Reset(); }); actionChain.EnqueueChain(); return; } if ((player.PkLevel == PKLevel.NPK && IsPKSwitch) || (player.PkLevel == PKLevel.PK && IsNPKSwitch)) { IsBusy = true; player.IsBusy = true; var useMotion = UseTargetSuccessAnimation != MotionCommand.Invalid ? UseTargetSuccessAnimation : MotionCommand.Twitch1; EnqueueBroadcastMotion(new Motion(this, useMotion)); var motionTable = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId); var useTime = motionTable.GetAnimationLength(useMotion); player.LastUseTime += useTime; var actionChain = new ActionChain(); actionChain.AddDelaySeconds(useTime); actionChain.AddAction(player, () => { player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.UseMessage), ChatMessageType.Broadcast)); player.PkLevelModifier += PkLevelModifier; if (player.PkLevel == PKLevel.PK) { player.PlayerKillerStatus = PlayerKillerStatus.PK; } else { player.PlayerKillerStatus = PlayerKillerStatus.NPK; } player.EnqueueBroadcast(new GameMessagePublicUpdatePropertyInt(player, PropertyInt.PlayerKillerStatus, (int)player.PlayerKillerStatus)); //player.ApplySoundEffects(Sound.Open); // in pcaps, but makes no sound/has no effect. ? player.IsBusy = false; Reset(); }); actionChain.EnqueueChain(); } else { player.Session.Network.EnqueueSend(new GameMessageSystemChat(GetProperty(PropertyString.ActivationFailure), ChatMessageType.Broadcast)); } }
public static void HandleHouseSelect(Session session, bool confirmed, params string[] parameters) { if (!int.TryParse(parameters[0], out var houseIdx)) { return; } // ensure current multihouse owner if (!session.Player.IsMultiHouseOwner(false)) { log.Warn($"{session.Player.Name} tried to /house-select {houseIdx}, but they are not currently a multi-house owner!"); return; } // get house info for this index var multihouses = session.Player.GetMultiHouses(); if (houseIdx < 1 || houseIdx > multihouses.Count) { session.Network.EnqueueSend(new GameMessageSystemChat($"Please enter a number between 1 and {multihouses.Count}.", ChatMessageType.Broadcast)); return; } var keepHouse = multihouses[houseIdx - 1]; // show confirmation popup if (!confirmed) { var houseType = $"{keepHouse.HouseType}".ToLower();; var loc = HouseManager.GetCoords(keepHouse.SlumLord.Location); var msg = $"Are you sure you want to keep the {houseType} at\n{loc}?"; session.Player.ConfirmationManager.EnqueueSend(new Confirmation_Custom(session.Player.Guid, () => HandleHouseSelect(session, true, parameters)), msg); return; } // house to keep confirmed, abandon the other houses var abandonHouses = new List <House>(multihouses); abandonHouses.RemoveAt(houseIdx - 1); foreach (var abandonHouse in abandonHouses) { var house = session.Player.GetHouse(abandonHouse.Guid.Full); HouseManager.HandleEviction(house, house.HouseOwner ?? 0, true); } // set player properties for house to keep var player = PlayerManager.FindByGuid(keepHouse.HouseOwner ?? 0, out bool isOnline); if (player == null) { log.Error($"{session.Player.Name}.HandleHouseSelect({houseIdx}) - couldn't find HouseOwner {keepHouse.HouseOwner} for {keepHouse.Name} ({keepHouse.Guid})"); return; } player.HouseId = keepHouse.HouseId; player.HouseInstance = keepHouse.Guid.Full; player.SaveBiotaToDatabase(); // update house panel for current player var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); // wait for slumlord inventory biotas above to save actionChain.AddAction(session.Player, session.Player.HandleActionQueryHouse); actionChain.EnqueueChain(); Console.WriteLine("OK"); }
/// <summary> /// Performs a melee attack for the monster /// </summary> /// <returns>The length in seconds for the attack animation</returns> public float MeleeAttack() { var target = AttackTarget as Creature; if (target == null || !target.IsAlive) { Sleep(); return(0.0f); } // choose a random combat maneuver var maneuver = GetCombatManeuver(); if (maneuver == null) { Console.WriteLine($"Combat maneuver null! Stance {CurrentMotionState.Stance}, MotionTable {MotionTableId:X8}"); return(0.0f); } AttackHeight = maneuver.AttackHeight; // select random body part @ current attack height var bodyPart = BodyParts.GetBodyPart(AttackHeight.Value); DoSwingMotion(AttackTarget, maneuver, out float animLength); PhysicsObj.stick_to_object(AttackTarget.PhysicsObj.ID); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(animLength / 3.0f); // TODO: get attack frame? actionChain.AddAction(this, () => { if (AttackTarget == null) { return; } var critical = false; var damageType = DamageType.Undef; var shieldMod = 1.0f; var damage = CalculateDamage(ref damageType, maneuver, bodyPart, ref critical, ref shieldMod); var player = AttackTarget as Player; if (damage > 0.0f) { player.TakeDamage(this, damageType, damage, bodyPart, critical); if (shieldMod != 1.0f) { var shieldSkill = player.GetCreatureSkill(Skill.Shield); Proficiency.OnSuccessUse(player, shieldSkill, shieldSkill.Current); // ? } } else { player.OnEvade(this, AttackType.Melee); } }); actionChain.EnqueueChain(); // TODO: figure out exact speed / delay formula var meleeDelay = Physics.Common.Random.RollDice(MeleeDelayMin, MeleeDelayMax); NextAttackTime = Timers.RunningTime + animLength + meleeDelay;; return(animLength); }
/// <summary> /// Called every ~5 secs for equipped mana consuming items /// </summary> public void ManaConsumersTick() { if (!EquippedObjectsLoaded) { return; } var EquippedManaConsumers = EquippedObjects.Where(k => (k.Value.IsAffecting ?? false) && //k.Value.ManaRate.HasValue && k.Value.ItemMaxMana.HasValue && k.Value.ItemCurMana.HasValue && k.Value.ItemCurMana.Value > 0).ToList(); foreach (var k in EquippedManaConsumers) { var item = k.Value; // this was a bug in lootgen until 7/11/2019, mostly for clothing/armor/shields // tons of existing items on servers are in this bugged state, where they aren't ticking mana. // this retroactively fixes them when equipped // items such as Impious Staff are excluded from this via IsAffecting if (item.ManaRate == null) { item.ManaRate = LootGenerationFactory.GetManaRate(item); log.Warn($"{Name}.ManaConsumersTick(): {k.Value.Name} ({k.Value.Guid}) fixed missing ManaRate"); } var rate = item.ManaRate.Value; if (LumAugItemManaUsage != 0) { rate *= GetNegativeRatingMod(LumAugItemManaUsage * 5); } if (!item.ItemManaConsumptionTimestamp.HasValue) { item.ItemManaConsumptionTimestamp = DateTime.UtcNow; } DateTime mostRecentBurn = item.ItemManaConsumptionTimestamp.Value; var timePerBurn = -1 / rate; var secondsSinceLastBurn = (DateTime.UtcNow - mostRecentBurn).TotalSeconds; var delta = secondsSinceLastBurn / timePerBurn; var deltaChopped = (int)Math.Floor(delta); var deltaExtra = delta - deltaChopped; if (deltaChopped <= 0) { continue; } var timeToAdd = (int)Math.Floor(deltaChopped * timePerBurn); item.ItemManaConsumptionTimestamp = mostRecentBurn + new TimeSpan(0, 0, timeToAdd); var manaToBurn = Math.Min(item.ItemCurMana.Value, deltaChopped); deltaChopped = Math.Clamp(deltaChopped, 0, 10); item.ItemCurMana -= deltaChopped; if (item.ItemCurMana < 1 || item.ItemCurMana == null) { item.IsAffecting = false; var msg = new GameMessageSystemChat($"Your {item.Name} is out of Mana.", ChatMessageType.Magic); var sound = new GameMessageSound(Guid, Sound.ItemManaDepleted); Session.Network.EnqueueSend(msg, sound); if (item.WielderId != null) { if (item.Biota.PropertiesSpellBook != null) { // unsure if these messages / sounds were ever sent in retail, // or if it just purged the enchantments invisibly // doing a delay here to prevent 'SpellExpired' sounds from overlapping with 'ItemManaDepleted' var actionChain = new ActionChain(); actionChain.AddDelaySeconds(2.0f); actionChain.AddAction(this, () => { foreach (var spellId in item.Biota.GetKnownSpellsIds(item.BiotaDatabaseLock)) { RemoveItemSpell(item, (uint)spellId); } }); actionChain.EnqueueChain(); } } } else { // get time until empty var secondsUntilEmpty = ((item.ItemCurMana - deltaExtra) * timePerBurn); if (secondsUntilEmpty <= 120 && (!item.ItemManaDepletionMessageTimestamp.HasValue || (DateTime.UtcNow - item.ItemManaDepletionMessageTimestamp.Value).TotalSeconds > 120)) { item.ItemManaDepletionMessageTimestamp = DateTime.UtcNow; Session.Network.EnqueueSend(new GameMessageSystemChat($"Your {item.Name} is low on Mana.", ChatMessageType.Magic)); } } } }
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; var difficulty = 0; if (unlocker.WeenieType == WeenieType.Lockpick) { var effectiveLockpickSkill = GetEffectiveLockpickSkill(player, unlocker); result = @lock.Unlock(player.Guid.Full, effectiveLockpickSkill, ref difficulty); } 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(player.Guid.Full, woKey); } switch (result) { case UnlockResults.UnlockSuccess: if (unlocker.WeenieType == WeenieType.Lockpick) { // the source guid for this sound must be the player, else the sound will not play // which differs from PicklockFail and LockSuccess being in the target sound table player.EnqueueBroadcast(new GameMessageSound(player.Guid, Sound.Lockpicking, 1.0f)); var lockpickSkill = player.GetCreatureSkill(Skill.Lockpick); Proficiency.OnSuccessUse(player, lockpickSkill, difficulty); } ConsumeUnlocker(player, unlocker, target, true); 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.EnqueueBroadcast(new GameMessageSound(target.Guid, Sound.PicklockFail, 1.0f)); ConsumeUnlocker(player, unlocker, target, false); 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(); }
private ActionChain CreateMoveToChain(ObjectGuid target, out int thisMoveToChainNumber) { thisMoveToChainNumber = GetNextMoveToChainNumber(); ActionChain moveToChain = new ActionChain(); moveToChain.AddAction(this, () => { var targetObject = CurrentLandblock?.GetObject(target); if (targetObject == null) { // Is the item we're trying to move to in the container we have open? var lastUsedContainer = CurrentLandblock?.GetObject(lastUsedContainerId) as Container; if (lastUsedContainer != null) { if (lastUsedContainer.Inventory.ContainsKey(target)) { targetObject = lastUsedContainer; } else { // could be a child container of this container log.Error("Player_Use CreateMoveToChain container inception not finished"); return; } } } if (targetObject == null) { log.Error("Player_Use CreateMoveToChain targetObject null"); return; } if (targetObject.Location == null) { log.Error("Player_Use CreateMoveToChain targetObject.Location null"); return; } if (targetObject.WeenieType == WeenieType.Portal) { OnAutonomousMove(targetObject.Location, Sequences, MovementTypes.MoveToPosition, target, (targetObject.UseRadius ?? 0)); } else { OnAutonomousMove(targetObject.Location, Sequences, MovementTypes.MoveToObject, target, (targetObject.UseRadius ?? 0)); } }); // poll for arrival every .1 seconds ActionChain moveToBody = new ActionChain(); moveToBody.AddDelaySeconds(.1); var thisMoveToChainNumberCopy = thisMoveToChainNumber; moveToChain.AddLoop(this, () => { if (thisMoveToChainNumberCopy != moveToChainCounter) { return(false); } // Break loop if CurrentLandblock == null (we portaled or logged out) if (CurrentLandblock == null) { return(false); } // Are we within use radius? var valid = false; bool ret = CurrentLandblock != null ? !CurrentLandblock.WithinUseRadius(this, target, out valid) : false; // If one of the items isn't on a landblock if (!valid) { ret = false; } return(ret); }, moveToBody); return(moveToChain); }
private static void DoPlayerEnterWorld(Session session, Character character, Biota playerBiota, PossessedBiotas possessedBiotas) { Player player; Player.HandleNoLogLandblock(playerBiota); var stripAdminProperties = false; var addAdminProperties = false; var addSentinelProperties = false; if (ConfigManager.Config.Server.Accounts.OverrideCharacterPermissions) { if (session.AccessLevel <= AccessLevel.Advocate) // check for elevated characters { if (playerBiota.WeenieType == (int)WeenieType.Admin || playerBiota.WeenieType == (int)WeenieType.Sentinel) // Downgrade weenie { character.IsPlussed = false; playerBiota.WeenieType = (int)WeenieType.Creature; stripAdminProperties = true; } } else if (session.AccessLevel >= AccessLevel.Sentinel && session.AccessLevel <= AccessLevel.Envoy) { if (playerBiota.WeenieType == (int)WeenieType.Creature || playerBiota.WeenieType == (int)WeenieType.Admin) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = (int)WeenieType.Sentinel; addSentinelProperties = true; } } else // Developers and Admins { if (playerBiota.WeenieType == (int)WeenieType.Creature || playerBiota.WeenieType == (int)WeenieType.Sentinel) // Up/downgrade weenie { character.IsPlussed = true; playerBiota.WeenieType = (int)WeenieType.Admin; addAdminProperties = true; } } } if (playerBiota.WeenieType == (int)WeenieType.Admin) { player = new Admin(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else if (playerBiota.WeenieType == (int)WeenieType.Sentinel) { player = new Sentinel(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } else { player = new Player(playerBiota, possessedBiotas.Inventory, possessedBiotas.WieldedItems, character, session); } session.SetPlayer(player); if (stripAdminProperties) // continue stripping properties { player.CloakStatus = null; player.Attackable = true; player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, true); player.AdvocateLevel = null; player.ChannelsActive = null; player.ChannelsAllowed = null; player.Invincible = false; player.Cloaked = null; player.IgnoreHouseBarriers = false; player.IgnorePortalRestrictions = false; player.SafeSpellComponents = false; player.ChangesDetected = true; player.CharacterChangesDetected = true; } if (addSentinelProperties || addAdminProperties) // continue restoring properties to default { WorldObject weenie; if (addAdminProperties) { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("admin"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)); } else { weenie = Factories.WorldObjectFactory.CreateWorldObject(DatabaseManager.World.GetCachedWeenie("sentinel"), new ACE.Entity.ObjectGuid(ACE.Entity.ObjectGuid.Invalid.Full)); } if (weenie != null) { player.CloakStatus = CloakStatus.Off; player.Attackable = weenie.Attackable; player.SetProperty(ACE.Entity.Enum.Properties.PropertyBool.DamagedByCollisions, false); player.AdvocateLevel = weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.AdvocateLevel); player.ChannelsActive = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsActive); player.ChannelsAllowed = (Channel?)weenie.GetProperty(ACE.Entity.Enum.Properties.PropertyInt.ChannelsAllowed); player.Invincible = false; player.Cloaked = false; player.ChangesDetected = true; player.CharacterChangesDetected = true; } } // If the client is missing a location, we start them off in the starter town they chose if (session.Player.Location == null) { if (session.Player.Instantiation != null) { session.Player.Location = new Position(session.Player.Instantiation); } else { session.Player.Location = new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); // ultimate fallback; } } session.Player.PlayerEnterWorld(); var success = LandblockManager.AddObject(session.Player, true); if (!success) { // send to lifestone, or fallback location var fixLoc = session.Player.Sanctuary ?? new Position(0xA9B40019, 84, 7.1f, 94, 0, 0, -0.0784591f, 0.996917f); log.Error($"WorldManager.DoPlayerEnterWorld: failed to spawn {session.Player.Name}, relocating to {fixLoc.ToLOCString()}"); session.Player.Location = new Position(fixLoc); LandblockManager.AddObject(session.Player, true); var actionChain = new ActionChain(); actionChain.AddDelaySeconds(5.0f); actionChain.AddAction(session.Player, () => { if (session != null && session.Player != null) { session.Player.Teleport(fixLoc); } }); actionChain.EnqueueChain(); } var popup_header = PropertyManager.GetString("popup_header").Item; var popup_motd = PropertyManager.GetString("popup_motd").Item; var popup_welcome = PropertyManager.GetString("popup_welcome").Item; if (character.TotalLogins <= 1) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd, popup_welcome))); } else if (!string.IsNullOrEmpty(popup_motd)) { session.Network.EnqueueSend(new GameEventPopupString(session, AppendLines(popup_header, popup_motd))); } var info = "Welcome to Asheron's Call\n powered by ACEmulator\n\nFor more information on commands supported by this server, type @acehelp\n"; session.Network.EnqueueSend(new GameMessageSystemChat(info, ChatMessageType.Broadcast)); var server_motd = PropertyManager.GetString("server_motd").Item; if (!string.IsNullOrEmpty(server_motd)) { session.Network.EnqueueSend(new GameMessageSystemChat($"{server_motd}\n", ChatMessageType.Broadcast)); } }
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(); }
public void ActOnJoin_Legacy(Player player) { if (active) { return; } active = true; // team is either 0 or 1. -1 means failed to join var msgJoinResponse = new GameEventJoinGameResponse(player.Session, Guid, ChessColor.Black); // 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, 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, new ObjectGuid(0), -2); player.Session.Network.EnqueueSend(popupGDL, msgGameOver2); player.ChessGamesLost++; player.ChessTotalGames++; active = false; }); gdlChain.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(); 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("Your house has reverted due to non-payment of the maintenance costs. All items stored in the house have been lost.", 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(); }
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; } } // 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(); // Init the client with the chat channel ID's, and then notify the player that they've choined the associated channels. UpdateChatChannels(); var general = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "General"); var trade = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "Trade"); var lfg = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "LFG"); var roleplay = new GameEventWeenieErrorWithString(Session, WeenieErrorWithString.YouHaveEnteredThe_Channel, "Roleplay"); Session.Network.EnqueueSend(general, trade, lfg, roleplay); // 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(); HandleMissingXp(); HandleSkillCreditRefund(); if (PlayerKillerStatus == PlayerKillerStatus.PKLite && !PropertyManager.GetBool("pkl_server").Item) { var actionChain = new ActionChain(); actionChain.AddDelaySeconds(3.0f); actionChain.AddAction(this, () => { UpdateProperty(this, PropertyInt.PlayerKillerStatus, (int)PlayerKillerStatus.NPK, true); Session.Network.EnqueueSend(new GameEventWeenieError(Session, WeenieError.YouAreNonPKAgain)); }); actionChain.EnqueueChain(); } HandleDBUpdates(); }
/// <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(); } } }
private static void HandleHealingRecipe(Player player, WorldObject source, WorldObject target, Recipe recipe) { ActionChain chain = new ActionChain(); // skill will be null since the difficulty is calculated manually if (recipe.SkillId == null) { log.Warn($"healing recipe has null skill id (should almost certainly be healing, but who knows). recipe id {recipe.RecipeId}."); player.SendUseDoneEvent(); return; } if (!(target is Player)) { var message = new GameMessageSystemChat($"The {source.Name} cannot be used on {target.Name}.", ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); player.SendUseDoneEvent(); return; } Player targetPlayer = target as Player; Ability vital = (Ability?)recipe.HealingAttribute ?? Ability.Health; // there's a skill associated with this Skill skillId = (Skill)recipe.SkillId.Value; // this shouldn't happen, but sanity check for unexpected nulls if (!player.Skills.ContainsKey(skillId)) { log.Warn("Unexpectedly missing skill in Recipe usage"); player.SendUseDoneEvent(); return; } CreatureSkill skill = player.Skills[skillId]; // at this point, we've validated that the target is a player, and the target is below max health if (target.Guid != player.Guid) { // TODO: validate range } MotionCommand cmd = MotionCommand.SkillHealSelf; if (target.Guid != player.Guid) { cmd = MotionCommand.Woah; // guess? nothing else stood out } // everything pre-validatable is validated. action will be attempted unless cancelled, so // queue up the animation and action UniversalMotion motion = new UniversalMotion(MotionStance.Standing, new MotionItem(cmd)); chain.AddAction(player, () => player.HandleActionMotion(motion)); chain.AddDelaySeconds(0.5); chain.AddAction(player, () => { // TODO: revalidate range if other player (they could have moved) double difficulty = 2 * (targetPlayer.Vitals[vital].MaxValue - targetPlayer.Vitals[vital].Current); if (difficulty <= 0) { // target is at max (or higher?) health, do nothing var text = "You are already at full health."; if (target.Guid != player.Guid) { text = $"{target.Name} is already at full health"; } player.Session.Network.EnqueueSend(new GameMessageSystemChat(text, ChatMessageType.Craft)); player.SendUseDoneEvent(); return; } if (player.CombatMode != CombatMode.NonCombat && player.CombatMode != CombatMode.Undef) { difficulty *= 1.1; } uint boost = source.Boost ?? 0; double multiplier = source.HealkitMod ?? 1; double playerSkill = skill.ActiveValue + boost; if (skill.Status == SkillStatus.Trained) { playerSkill *= 1.1; } else if (skill.Status == SkillStatus.Specialized) { playerSkill *= 1.5; } // usage is inevitable at this point, consume the use if ((recipe.ResultFlags & (uint)RecipeResult.SourceItemUsesDecrement) > 0) { if (source.Structure <= 1) { player.DestroyInventoryItem(source); } else { source.Structure--; source.SendPartialUpdates(player.Session, _updateStructure); } } double percentSuccess = CreatureSkill.GetPercentSuccess((uint)playerSkill, (uint)difficulty); if (_random.NextDouble() <= percentSuccess) { string expertly = ""; if (_random.NextDouble() < 0.1d) { expertly = "expertly "; multiplier *= 1.2; } // calculate amount restored uint maxRestore = targetPlayer.Vitals[vital].MaxValue - targetPlayer.Vitals[vital].Current; // TODO: get actual forumula for healing. this is COMPLETELY wrong. this is 60 + random(1-60). double amountRestored = 60d + _random.Next(1, 61); amountRestored *= multiplier; uint actualRestored = (uint)Math.Min(maxRestore, amountRestored); targetPlayer.Vitals[vital].Current += actualRestored; var updateVital = new GameMessagePrivateUpdateAttribute2ndLevel(player.Session, vital.GetVital(), targetPlayer.Vitals[vital].Current); player.Session.Network.EnqueueSend(updateVital); if (targetPlayer.Guid != player.Guid) { // tell the other player they got healed var updateVitalToTarget = new GameMessagePrivateUpdateAttribute2ndLevel(targetPlayer.Session, vital.GetVital(), targetPlayer.Vitals[vital].Current); targetPlayer.Session.Network.EnqueueSend(updateVitalToTarget); } string name = "yourself"; if (targetPlayer.Guid != player.Guid) { name = targetPlayer.Name; } string vitalName = "Health"; if (vital == Ability.Stamina) { vitalName = "Stamina"; } else if (vital == Ability.Mana) { vitalName = "Mana"; } string uses = source.Structure == 1 ? "use" : "uses"; var text = string.Format(recipe.SuccessMessage, expertly, name, actualRestored, vitalName, source.Name, source.Structure, uses); var message = new GameMessageSystemChat(text, ChatMessageType.Craft); player.Session.Network.EnqueueSend(message); if (targetPlayer.Guid != player.Guid) { // send text to the other player too text = string.Format(recipe.AlternateMessage, player.Name, expertly, actualRestored, vitalName); message = new GameMessageSystemChat(text, ChatMessageType.Craft); targetPlayer.Session.Network.EnqueueSend(message); } } player.SendUseDoneEvent(); }); chain.EnqueueChain(); }