public void HandlePKDeathBroadcast(DamageHistoryInfo lastDamager, DamageHistoryInfo topDamager) { if (topDamager == null || !topDamager.IsPlayer) { return; } var pkPlayer = topDamager.TryGetAttacker() as Player; if (pkPlayer == null) { return; } if (IsPKDeath(topDamager)) { pkPlayer.PkTimestamp = Time.GetUnixTime(); pkPlayer.PlayerKillsPk++; var globalPKDe = $"{lastDamager.Name} has defeated {Name}!"; if ((Location.Cell & 0xFFFF) < 0x100) { globalPKDe += $" The kill occured at {Location.GetMapCoordStr()}"; } globalPKDe += "\n[PKDe]"; PlayerManager.BroadcastToAll(new GameMessageSystemChat(globalPKDe, ChatMessageType.Broadcast)); } else if (IsPKLiteDeath(topDamager)) { pkPlayer.PlayerKillsPkl++; } }
public void HandlePKDeathBroadcast(DamageHistoryInfo lastDamager, DamageHistoryInfo topDamager) { if (topDamager == null || !topDamager.IsPlayer) { return; } var pkPlayer = topDamager.TryGetAttacker() as Player; if (pkPlayer == null) { return; } if (IsPKDeath(topDamager)) { pkPlayer.PkTimestamp = Time.GetUnixTime(); pkPlayer.PlayerKillsPk++; string globalPKDe; if (pkPlayer.CurrentLandblock.RealmHelpers.IsDuel) { globalPKDe = $"{lastDamager.Name} has defeated {Name} in a duel!"; } else { globalPKDe = $"{lastDamager.Name} has defeated {Name}!"; if (!Location.Indoors) { globalPKDe += $" The kill occured at {Location.GetMapCoordStr()}"; } } globalPKDe += "\n[PKDe]"; PlayerManager.BroadcastToAll(new GameMessageSystemChat(globalPKDe, ChatMessageType.Broadcast)); } else if (IsPKLiteDeath(topDamager)) { pkPlayer.PlayerKillsPkl++; } }
public static void SendDeathDetailsViaHTTP(DamageHistoryInfo topDamager, DamageHistoryInfo killshot, WorldObject victim) { log.Info($"KILLSHOT. killer:{topDamager.Guid.Full}, victim:{victim.Guid.Full}, finisher:{killshot.Guid.Full}"); var web_portal_api_killshot_on = PropertyManager.GetBool("web_portal_api_killshot_on").Item; if (web_portal_api_killshot_on) { var web_portal_url = PropertyManager.GetString("web_portal_url").Item; var web_portal_api_version = PropertyManager.GetString("web_portal_api_version").Item; var web_portal_api_jwt = PropertyManager.GetString("web_portal_api_jwt").Item; if (!string.IsNullOrEmpty(web_portal_url) || !string.IsNullOrEmpty(web_portal_api_version) || !string.IsNullOrEmpty(web_portal_api_jwt)) { var client = new RestClient(web_portal_url); // client.Authenticator = new HttpBasicAuthenticator(username, password); var request = new RestRequest("killshot" + web_portal_api_version + "/deaths", Method.POST); request.AddHeader("Content-type", "application/json"); request.AddHeader("Authorization", web_portal_api_jwt); request.AddJsonBody(new { killer = topDamager.Guid.Full, victim = victim.Guid.Full, finisher = killshot.Guid.Full, }); // easy async support client.ExecuteAsync(request, response => { // Nothing here var x = 1; }); } else { log.Info("ERROR: Riptide API Web Portal not initialized"); } } }
/// <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(DamageHistoryInfo lastDamager, DamageType damageType, bool criticalHit = false) { var topDamager = DamageHistory.GetTopDamager(false); HandlePKDeathBroadcast(lastDamager, topDamager); var deathMessage = base.OnDeath(lastDamager, damageType, criticalHit); var lastDamagerObj = lastDamager?.TryGetAttacker(); if (lastDamagerObj != null) { lastDamagerObj.EmoteManager.OnKill(this); } var playerMsg = ""; if (lastDamager != null) { playerMsg = string.Format(deathMessage.Victim, Name, lastDamager.Name); } else { playerMsg = deathMessage.Victim; } var msgYourDeath = new GameEventVictimNotification(Session, playerMsg); Session.Network.EnqueueSend(msgYourDeath); // broadcast to nearby players var nearbyMsg = ""; if (lastDamager != null) { nearbyMsg = string.Format(deathMessage.Broadcast, Name, lastDamager.Name); } else { nearbyMsg = deathMessage.Broadcast; } var broadcastMsg = new GameMessagePlayerKilled(nearbyMsg, Guid, lastDamager?.Guid ?? ObjectGuid.Invalid); log.Debug("[CORPSE] " + nearbyMsg); var excludePlayers = new List <Player>(); var nearbyPlayers = EnqueueBroadcast(excludePlayers, false, broadcastMsg); excludePlayers.AddRange(nearbyPlayers); if (Fellowship != null) { Fellowship.OnDeath(this); } // if the player's lifestone is in a different landblock, also broadcast their demise to that landblock if (PropertyManager.GetBool("lifestone_broadcast_death").Item&& 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, Sanctuary, LocalBroadcastRangeSq, broadcastMsg); } return(deathMessage); }
/// <summary> /// Broadcasts the player death animation, updates vitae, and sends network messages for player death /// Queues the action to call TeleportOnDeath and enter portal space soon /// </summary> protected override void Die(DamageHistoryInfo lastDamager, DamageHistoryInfo topDamager) { if (topDamager?.Guid == Guid && IsPKType) { var topDamagerOther = DamageHistory.GetTopDamager(false); if (topDamagerOther != null && topDamagerOther.IsPlayer) { topDamager = topDamagerOther; } } UpdateVital(Health, 0); NumDeaths++; suicideInProgress = false; if (CombatMode == CombatMode.Magic && MagicState.IsCasting) { FailCast(false); } // TODO: instead of setting IsBusy here, // eventually all of the places that check for states such as IsBusy || Teleporting // might want to use a common function, and IsDead should return a separate error IsBusy = true; // killer = top damager for looting rights if (topDamager != null) { KillerId = topDamager.Guid.Full; } // broadcast death animation var deathAnim = new Motion(MotionStance.NonCombat, MotionCommand.Dead); EnqueueBroadcastMotion(deathAnim); // create network messages for player death var msgHealthUpdate = new GameMessagePrivateUpdateAttribute2ndLevel(this, Vital.Health, 0); // TODO: death sounds? seems to play automatically in client // var msgDeathSound = new GameMessageSound(Guid, Sound.Death1, 1.0f); var msgNumDeaths = new GameMessagePrivateUpdatePropertyInt(this, PropertyInt.NumDeaths, NumDeaths); // send network messages for player death Session.Network.EnqueueSend(msgHealthUpdate, msgNumDeaths); if (lastDamager?.Guid == Guid) // suicide { var msgSelfInflictedDeath = new GameEventWeenieError(Session, WeenieError.YouKilledYourself); Session.Network.EnqueueSend(msgSelfInflictedDeath); } // update vitae // players who died in a PKLite fight do not accrue vitae if (!IsPKLiteDeath(topDamager)) { InflictVitaePenalty(); } if (IsPKDeath(topDamager) || AugmentationSpellsRemainPastDeath == 0) { var msgPurgeEnchantments = new GameEventMagicPurgeEnchantments(Session); EnchantmentManager.RemoveAllEnchantments(); Session.Network.EnqueueSend(msgPurgeEnchantments); } // wait for the death animation to finish var dieChain = new ActionChain(); var animLength = DatManager.PortalDat.ReadFromDat <MotionTable>(MotionTableId).GetAnimationLength(MotionCommand.Dead); dieChain.AddDelaySeconds(animLength + 1.0f); dieChain.AddAction(this, () => { CreateCorpse(topDamager); ThreadSafeTeleportOnDeath(); // enter portal space if (IsPKDeath(topDamager) || IsPKLiteDeath(topDamager)) { SetMinimumTimeSincePK(); } IsBusy = false; }); dieChain.EnqueueChain(); }