/// <summary> /// Called when the player enters portal space after dying /// </summary> public void ThreadSafeTeleportOnDeath() { // teleport to sanctuary or best location var newPosition = Sanctuary ?? Instantiation ?? Location; WorldManager.ThreadSafeTeleport(this, newPosition, new ActionEventDelegate(() => { // Stand back up SetCombatMode(CombatMode.NonCombat); SetLifestoneProtection(); var teleportChain = new ActionChain(); if (!IsLoggingOut) // If we're in the process of logging out, we skip the delay { 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(); OnHealthUpdate(); IsInDeathProcess = false; if (IsLoggingOut) { LogOut_Final(true); } }); teleportChain.EnqueueChain(); })); }
/// <summary> /// Called when a player dies, in conjunction with Die() /// </summary> /// <param name="lastDamager">The last damager that landed the death blow</param> /// <param name="damageType">The damage type for the death message</param> public override DeathMessage OnDeath(WorldObject lastDamager, DamageType damageType, bool criticalHit = false) { var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit); var playerMsg = string.Format(deathMessage.Victim, Name, lastDamager.Name); var msgYourDeath = new GameEventYourDeath(Session, playerMsg); Session.Network.EnqueueSend(msgYourDeath); // broadcast to nearby players var nearbyMsg = string.Format(deathMessage.Broadcast, Name, lastDamager.Name); var broadcastMsg = new GameMessageSystemChat(nearbyMsg, ChatMessageType.Broadcast); var nearbyPlayers = EnqueueBroadcast(false, broadcastMsg); var excludePlayers = nearbyPlayers.ToList(); excludePlayers.Add(this); // exclude self // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock if (Sanctuary != null && Location.Landblock != Sanctuary.Landblock) { // ActionBroadcastKill might not work if other players around lifestone aren't aware of this player yet... // this existing broadcast method is also based on the current visible objects to the player, // and the player hasn't entered portal space or teleported back to the lifestone yet, so this doesn't work //ActionBroadcastKill(nearbyMsg, Guid, lastDamager.Guid); // instead, we get all of the players in the lifestone landblock + adjacent landblocks, // and possibly limit that to some radius around the landblock? var lifestoneBlock = LandblockManager.GetLandblock(new LandblockId(Sanctuary.Landblock << 16 | 0xFFFF), true); lifestoneBlock.EnqueueBroadcast(excludePlayers, true, broadcastMsg); } // reset damage history for this player DamageHistory.Reset(); return(deathMessage); }
/// <summary> /// Called when the player enters portal space after dying /// </summary> public void TeleportOnDeath() { // teleport to sanctuary or best location var newPosition = Sanctuary ?? LastPortal ?? 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(); }
/// <summary> /// Broadcasts the player death animation, updates vitae, and sends network messages for player death /// Queues the action to call TeleportOnDeath and enter portal space soon /// </summary> protected override void Die(WorldObject lastDamager, WorldObject topDamager) { UpdateVital(Health, 0); NumDeaths++; DeathLevel = Level; // for calculating vitae XP VitaeCpPool = 0; // reset vitae XP earned // killer = top damager for looting rights if (topDamager != null) { Killer = topDamager.Guid.Full; } // broadcast death animation var deathAnim = new UniversalMotion(MotionStance.NonCombat, new MotionItem(MotionCommand.Dead)); EnqueueBroadcastMotion(deathAnim); // killer death message = last damager var killerMsg = lastDamager != null ? " to " + lastDamager.Name : ""; var currentDeathMessage = $"died{killerMsg}."; // create network messages for player death var msgHealthUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, 0); // TODO: death sounds? seems to play automatically in client // var msgDeathSound = new GameMessageSound(Guid, Sound.Death1, 1.0f); var msgYourDeath = new GameEventYourDeath(Session, $"You have {currentDeathMessage}"); var msgNumDeaths = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.NumDeaths, NumDeaths ?? 0); var msgDeathLevel = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.DeathLevel, DeathLevel ?? 0); var msgVitaeCpPool = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.VitaeCpPool, VitaeCpPool.Value); var msgPurgeEnchantments = new GameEventPurgeAllEnchantments(Session); // update vitae var vitae = EnchantmentManager.UpdateVitae(); var spellID = (uint)Network.Enum.Spell.Vitae; var spellBase = DatManager.PortalDat.SpellTable.Spells[spellID]; var spell = DatabaseManager.World.GetCachedSpell(spellID); var vitaeEnchantment = new Enchantment(this, Guid, spellID, (double)spell.Duration, 0, spell.StatModType, vitae); var msgVitaeEnchantment = new GameEventMagicUpdateEnchantment(Session, vitaeEnchantment); // send network messages for player death Session.Network.EnqueueSend(msgHealthUpdate, msgYourDeath, msgNumDeaths, msgDeathLevel, msgVitaeCpPool, msgPurgeEnchantments, msgVitaeEnchantment); // wait for the death animation to finish var dieChain = new ActionChain(); var animLength = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.Dead); dieChain.AddDelaySeconds(animLength + 1.0f); // enter portal space dieChain.AddAction(this, CreateCorpse); dieChain.AddAction(this, TeleportOnDeath); dieChain.EnqueueChain(); // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock if (Sanctuary != null && Location.Landblock != Sanctuary.Landblock) { var killerGuid = lastDamager != null ? lastDamager.Guid : Guid; ActionBroadcastKill($"{Name} has {currentDeathMessage}", Guid, killerGuid); } DamageHistory.Reset(); }