public static void ServerDespawnDrone(IDynamicWorldObject objectDrone, bool isReturnedToPlayer) { var privateState = objectDrone.GetPrivateState <DronePrivateState>(); var publicState = objectDrone.GetPublicState <DronePublicState>(); publicState.ResetTargetPosition(); if (privateState.IsDespawned) { return; } var droneItem = privateState.AssociatedItem; var protoItemDrone = (IProtoItemDrone)droneItem.ProtoItem; var characterOwner = privateState.CharacterOwner; var world = Server.World; var protoDrone = protoItemDrone.ProtoDrone; protoDrone.ServerOnDroneDroppedOrReturned(objectDrone, characterOwner, isReturnedToPlayer); // recreate physics (as despawned drone doesn't have any physics) privateState.IsDespawned = true; world.StopPhysicsBody(objectDrone.PhysicsBody); objectDrone.ProtoWorldObject.SharedCreatePhysics(objectDrone); world.SetPosition(objectDrone, ServerCharacterDeathMechanic.ServerGetGraveyardPosition().ToVector2D()); privateState.CharacterOwner = null; ServerOnDroneControlRemoved(characterOwner, objectDrone); var currentDurability = (int)(objectDrone.GetPublicState <DronePublicState>().StructurePointsCurrent / protoItemDrone.DurabilityToStructurePointsConversionCoefficient); if (currentDurability <= 1) { currentDurability = 0; } var deltaDurabilility = (int)(ItemDurabilitySystem.SharedGetDurabilityValue(droneItem) - currentDurability); if (deltaDurabilility <= 0) { return; } ItemDurabilitySystem.ServerModifyDurability(droneItem, -deltaDurabilility); if (droneItem.IsDestroyed) { // drone item degraded to 100%, notify the player ItemDurabilitySystem.Instance.CallClient(characterOwner, _ => _.ClientRemote_ItemBroke(droneItem.ProtoItem)); } }
protected override bool ServerIsCompleted(ICharacter character, PlayerTaskState state) { if (!character.ServerIsOnline || character.TilePosition == ServerCharacterDeathMechanic.ServerGetGraveyardPosition()) { return(false); } return(character.Tile.ProtoTile == this.ProtoTile); }
private static void ServerUpdate() { var serverTime = Server.Game.FrameTime; foreach (var character in Server.Characters.EnumerateAllPlayerCharacters(onlyOnline: false)) { if (ServerIsNeedDespawn(character, serverTime)) { ServerCharacterDeathMechanic.DespawnCharacter(character); } } }
public static void ServerPutIntoGarage(IDynamicWorldObject vehicle) { var position = ServerCharacterDeathMechanic.ServerGetGraveyardPosition().ToVector2D(); var vehiclePrivateState = vehicle.GetPrivateState <VehiclePrivateState>(); if (vehiclePrivateState.IsInGarage && vehicle.Position == position) { // already in garage return; } var vehicleCurrentPilot = vehicle.GetPublicState <VehiclePublicState>().PilotCharacter; if (vehicleCurrentPilot != null) { VehicleSystem.ServerCharacterExitCurrentVehicle(vehicleCurrentPilot, force: true); } vehiclePrivateState.IsInGarage = true; Server.World.SetPosition(vehicle, position, writeToLog: false); VehicleSystem.ServerResetLastVehicleMapMark(vehiclePrivateState); vehicle.ProtoWorldObject.SharedCreatePhysics(vehicle); Logger.Important("Vehicle put into the garage: " + vehicle, characterRelated: ServerRemoteContext.IsRemoteCall ? ServerRemoteContext.Character : null); if (ServerRemoteContext.IsRemoteCall) { // this action is done by player return; } // server put vehicle into garage - notify owners foreach (var owner in vehiclePrivateState.Owners) { var player = Server.Characters.GetPlayerCharacter(owner); if (player == null) { continue; } var protoVehicle = (IProtoVehicle)vehicle.ProtoGameObject; Instance.CallClient(player, _ => _.ClientRemote_VehicleInGarage(protoVehicle)); } }
public void ServerReduceHealth(double damage, IGameObjectWithProto damageSource) { if (damage <= 0) { return; } if (this.HealthCurrent <= 0) { return; } if (damageSource != null) { // it's important to register the damage source before the damage is applied // (to use it in case of the subsequent death) CharacterDamageTrackingSystem.ServerRegisterDamage(damage, (ICharacter)this.GameObject, new ServerDamageSourceEntry(damageSource)); } var newHealth = this.HealthCurrent - damage; if (newHealth <= 0 && ((ICharacter)this.GameObject).IsNpc && damageSource?.ProtoGameObject is IProtoStatusEffect) { var attackerCharacter = GetAttackerCharacter(damageSource, out _); if (attackerCharacter is null || attackerCharacter.IsNpc) { // Don't allow killing mob by a status effect which is NOT added by a player character. // This is a workaround to kill quests which cannot be finished // when creature is killed by a status effect. // TODO: Should be removed when we enable the damage tracking for mobs damage. newHealth = float.Epsilon; } } this.ServerSetHealthCurrent((float)newHealth); if (newHealth <= 0) { var attackerCharacter = GetAttackerCharacter(damageSource, out var weaponSkill); ServerCharacterDeathMechanic.OnCharacterKilled( attackerCharacter, targetCharacter: (ICharacter)this.GameObject, weaponSkill); } }
public void ServerReduceHealth(double damage, IGameObjectWithProto damageSource) { if (damage <= 0) { return; } if (this.HealthCurrent <= 0) { return; } var damagedCharacter = (ICharacter)this.GameObject; if (damageSource is not null) { // it's important to register the damage source before the damage is applied // (to use it in case of the subsequent death) CharacterDamageTrackingSystem.ServerRegisterDamage(damage, damagedCharacter, new ServerDamageSourceEntry(damageSource)); } var newHealth = this.HealthCurrent - damage; if (newHealth <= 0 && damagedCharacter.IsNpc && damageSource?.ProtoGameObject is IProtoStatusEffect) { var attackerCharacter = GetAttackerCharacter(damageSource, out _); if (attackerCharacter is null || attackerCharacter.IsNpc) { // don't allow killing a mob by a status effect which is NOT added by a player character newHealth = float.Epsilon; } } this.ServerSetHealthCurrent((float)newHealth); if (newHealth <= 0) { var attackerCharacter = GetAttackerCharacter(damageSource, out var weaponSkill); ServerCharacterDeathMechanic.OnCharacterKilled( attackerCharacter, targetCharacter: damagedCharacter, weaponSkill); } }
public static void SpawnPlayer(ICharacter character, bool isRespawn) { ServerAddTorchItemIfNoItems(character); if (character.IsInitialized && CharacterCreationSystem.SharedIsCharacterCreated(character)) { PlacePlayer(character, isRespawn); } else { // first time spawn, requires character creation ServerCharacterDeathMechanic.DespawnCharacter(character); } }
/// <summary> /// Set health - it will be clamped automatically. /// </summary> public void ServerSetHealthCurrent(float health, bool overrideDeath = false) { this.SharedTryRefreshFinalCache(); var character = (ICharacter)this.GameObject; var characterPublicState = character.GetPublicState <ICharacterPublicState>(); if (characterPublicState.IsDead && !overrideDeath) { // cannot change health this way for the dead character return; } health = MathHelper.Clamp(health, min: 0, max: this.HealthMax); if (this.HealthCurrent == health && !overrideDeath) { return; } if (health < this.HealthCurrent) { if (!character.IsNpc && CharacterInvincibilitySystem.ServerIsInvincible(character)) { // don't apply damage - character is invincible //Api.Logger.Important( // $"Cannot reduce character health - {character}: character is in invincible mode"); health = MathHelper.Clamp(this.HealthCurrent, min: 0, max: this.HealthMax); } } this.HealthCurrent = health; if (health <= 0) { characterPublicState.IsDead = true; Api.Logger.Important("Character dead: " + character); ServerCharacterDeathMechanic.OnCharacterDeath(character); } else if (characterPublicState.IsDead) { characterPublicState.IsDead = false; Api.Logger.Important("Character is not dead anymore: " + character); } }
protected override void ServerInitialize(ServerInitializeData data) { base.ServerInitialize(data); if (!data.IsFirstTimeInit) { return; } var item = data.GameObject; var protoDrone = LazyProtoDrone.Value; var objectDrone = Server.World.CreateDynamicWorldObject( protoDrone, ServerCharacterDeathMechanic.ServerGetGraveyardPosition().ToVector2D()); protoDrone.ServerSetupAssociatedItem(objectDrone, item); data.PrivateState.WorldObjectDrone = objectDrone; }
public override void ServerInitialize(IServerConfiguration serverConfiguration) { base.ServerInitialize(serverConfiguration); CharacterStyleSystem.ServerCharacterAppearanceSelected += ServerCharacterAppearanceSelectedHandler; foreach (var character in Server.Characters.EnumerateAllPlayerCharacters(onlyOnline: false)) { var privateState = PlayerCharacter.GetPrivateState(character); if (!privateState.IsDespawned && !SharedIsCharacterCreated(character)) { // despawn all invalid characters ServerCharacterDeathMechanic.DespawnCharacter(character); } } }
protected sealed override void ServerInitialize(ServerInitializeData data) { base.ServerInitialize(data); var publicState = data.PublicState; var privateState = data.PrivateState; privateState.EnsureEverythingCreated(); publicState.ServerEnsureEverythingCreated(); publicState.IsDead = publicState.CurrentStats.HealthCurrent <= 0; ServerCharacterDeathMechanic.OnCharacterInitialize(data.GameObject); this.ServerPrepareCharacter(data); if (data.IsFirstTimeInit) { this.ServerInitializeCharacterFirstTime(data); } this.ServerInitializeCharacter(data); }
public static bool SharedOnDamageToCharacter( ICharacter targetCharacter, WeaponFinalCache weaponCache, double damageMultiplier, out double damageApplied) { var targetPublicState = targetCharacter.GetPublicState <ICharacterPublicState>(); var targetCurrentStats = targetPublicState.CurrentStats; if (targetCurrentStats.HealthCurrent <= 0) { // target character is dead, cannot apply damage to it damageApplied = 0; return(false); } if (Api.IsClient) { // we don't simulate the damage on the client side damageApplied = 0; return(true); } var attackerCharacter = weaponCache.Character; // calculate and apply damage on server var targetFinalStatsCache = targetCharacter.GetPrivateState <BaseCharacterPrivateState>() .FinalStatsCache; var totalDamage = ServerCalculateTotalDamage( weaponCache, targetCharacter, targetFinalStatsCache, damageMultiplier, clampDefenseTo1: true); if (totalDamage <= 0) { // damage suppressed by armor damageApplied = 0; return(true); } // Clamp the max receivable damage to x5 from the max health. // This will help in case when the too much damage is dealt (mega-bomb!) // to ensure the equipment will not receive excessive damaged. totalDamage = Math.Min(totalDamage, 5 * targetCurrentStats.HealthMax); // apply damage targetCurrentStats.ServerSetHealthCurrent((float)(targetCurrentStats.HealthCurrent - totalDamage)); Api.Logger.Info( $"Damage applied to {targetCharacter} by {attackerCharacter}:\n{totalDamage} dmg, current health {targetCurrentStats.HealthCurrent}/{targetCurrentStats.HealthMax}, {weaponCache.Weapon}"); if (targetCurrentStats.HealthCurrent <= 0) { // killed! ServerCharacterDeathMechanic.OnCharacterKilled( targetCharacter, attackerCharacter, weaponCache.Weapon, weaponCache.ProtoWeapon); } damageApplied = totalDamage; ServerApplyDamageToEquippedItems(targetCharacter, damageApplied); return(true); }