public static void OnCharacterDeath(ICharacter deadCharacter) { var publicState = deadCharacter.GetPublicState <ICharacterPublicState>(); if (!publicState.IsDead) { publicState.CurrentStats.ServerSetHealthCurrent(0); return; } // recreate physics (as dead character doesn't have any physics) deadCharacter.ProtoCharacter.SharedCreatePhysics(deadCharacter); if (deadCharacter.ProtoCharacter is IProtoCharacterMob protoCharacterMob) { Api.SafeInvoke(() => CharacterDeath?.Invoke(deadCharacter)); protoCharacterMob.ServerOnDeath(deadCharacter); return; } // player character death // remember the death position (useful for the respawn) var privateState = PlayerCharacter.GetPrivateState(deadCharacter); privateState.LastDeathPosition = deadCharacter.TilePosition; privateState.LastDeathTime = Api.Server.Game.FrameTime; ServerTimersSystem.AddAction( delaySeconds: PlayerTeleportToGraveyardDelaySeconds, () => { if (!publicState.IsDead) { // player has already respawned return; } CharacterDespawnSystem.ServerTeleportPlayerCharacterToServiceArea(deadCharacter); CharacterRespawnSystem.ServerRemoveStatusEffectsOnRespawn(deadCharacter); }); var isPvPdeath = CharacterDamageTrackingSystem.ServerGetPvPdamagePercent(deadCharacter) >= 0.5; // register death (required even if the player is not a newbie) NewbieProtectionSystem.ServerRegisterDeath(deadCharacter, isPvPdeath, out var shouldSufferDeathConsequences); if (shouldSufferDeathConsequences) { DropPlayerLoot(deadCharacter); } else { Api.Logger.Important("Player character is dead - newbie PvP case, no loot drop or other consequences", deadCharacter); } Api.SafeInvoke(() => CharacterDeath?.Invoke(deadCharacter)); }
// "Graveyard" is a technical area in the bottom right corner of the map. private static void TeleportDeadPlayerCharacterToGraveyard(ICharacter character) { if (character.IsNpc) { // only player characters are teleported to graveyard return; } var publicState = character.GetPublicState <ICharacterPublicState>(); if (!publicState.IsDead) { // player has been respawned return; } VehicleSystem.ServerCharacterExitCurrentVehicle(character, force: true); // disable the visual scope so the player cannot not see anyone and nobody could see the player Api.Server.Characters.SetViewScopeMode(character, isEnabled: false); var graveyardPosition = ServerGetGraveyardPosition(); if (character.TilePosition != graveyardPosition) { ServerWorld.SetPosition(character, (Vector2D)graveyardPosition); } CharacterRespawnSystem.ServerRemoveInvalidStatusEffects(character); }
// "Graveyard" is a technical area in the bottom right corner of the map. private static void TeleportDeadPlayerCharacterToGraveyard(ICharacter character) { if (character.IsNpc) { // only player characters are teleported to graveyard return; } var publicState = character.GetPublicState <ICharacterPublicState>(); if (!publicState.IsDead) { // player has been respawned return; } // disable the visual scope so the player cannot not see anyone and nobody could see the player Api.Server.Characters.SetViewScopeMode(character, isEnabled: false); var world = Api.Server.World; var worldBounds = world.WorldBounds; // teleport to bottom right corner of the map var position = new Vector2Ushort((ushort)(worldBounds.Offset.X + worldBounds.Size.X - 1), (ushort)(worldBounds.Offset.Y + 1)); if (character.TilePosition != position) { world.SetPosition(character, (Vector2D)position); } CharacterRespawnSystem.ServerRemoveInvalidStatusEffects(character); }
private static void ServerSpawnIfReady(ICharacter character) { if (SharedIsCharacterCreated(character)) { CharacterRespawnSystem.ServerOnCharacterCreated(character); } }
private void ServerTimerTickCallback() { var time = Server.Game.FrameTime; serverRequests.ProcessAndRemoveByPair( pair => { // process unstuck var character = pair.Key; var request = pair.Value; if (character.GetPublicState <ICharacterPublicState>() .IsDead) { // character is dead this.CallClient(character, _ => _.ClientRemote_UnstuckFailedDead()); return(true); // remove this request } if (request.InitialPosition.DistanceSquaredTo(character.Position) > MaxUnstuckMovementDistance * MaxUnstuckMovementDistance) { // character moved ServerNotifyUnstuckCancelledCharacterMoved(character); return(true); // remove this request } if (!SharedValidateCanUnstuck(character)) { return(true); // remove this request } if (request.UnstuckAfter > time) { // time is not expired yet return(false); // don't remove this request } // try to teleport player away but nearby if (!CharacterRespawnSystem.ServerTryPlaceCharacterNearby(character, character.Position)) { // try to simply respawn in starting area (only the character position will be changed) ServerPlayerSpawnManager.PlacePlayer(character, isRespawn: false); } Logger.Info( $"Character unstuck from {request.InitialPosition} to {character.TilePosition}", character); this.CallClient(character, _ => _.ClientRemote_UnstuckSuccessful()); return(true); // remove this request }, removeCallback: pair => { var character = pair.Key; ServerOnUnstuckRequestRemoved(character); }); }
/// <summary> /// Please do not invoke for the already spawned character. /// </summary> public static void ServerSpawnIfReady(ICharacter character) { if (!SharedIsCharacterCreated(character)) { return; } Api.SafeInvoke(() => ServerCharacterCreated?.Invoke(character)); CharacterRespawnSystem.ServerOnCharacterCreated(character); }
public static void OnCharacterInitialize(ICharacter character) { if (character.IsNpc) { return; } if (PlayerCharacter.GetPublicState(character).IsDead) { CharacterDespawnSystem.ServerTeleportPlayerCharacterToServiceArea(character); CharacterRespawnSystem.ServerRemoveStatusEffectsOnRespawn(character); } else if (PlayerCharacter.GetPrivateState(character).IsDespawned) { CharacterDespawnSystem.ServerTeleportPlayerCharacterToServiceArea(character); } }