public virtual double ServerCalculateTotalDamageByExplosive( ICharacter byCharacter, IStaticWorldObject targetStaticWorldObject) { var structureExplosiveDefenseCoef = targetStaticWorldObject.ProtoStaticWorldObject.StructureExplosiveDefenseCoef; structureExplosiveDefenseCoef = MathHelper.Clamp(structureExplosiveDefenseCoef, 0, 1); var explosiveDefensePenetrationCoef = this.StructureDefensePenetrationCoef; explosiveDefensePenetrationCoef = MathHelper.Clamp(explosiveDefensePenetrationCoef, 0, 1); if (!PveSystem.SharedIsAllowStaticObjectDamage(byCharacter, targetStaticWorldObject, showClientNotification: false)) { return(0); } var damage = this.StructureDamage * (1 - structureExplosiveDefenseCoef * (1 - explosiveDefensePenetrationCoef)); return(damage); }
public static bool ClientTryStartDrone( IItem itemDrone, Vector2Ushort worldPosition, bool showErrorNotification) { var character = ClientCurrentCharacterHelper.Character; if (SharedIsMaxDronesToControlNumberExceeded(character, showErrorNotification) || SharedIsTargetAlreadyScheduledForAnyActiveDrone(character, worldPosition, showErrorNotification)) { return(false); } var targetObject = SharedGetCompatibleTarget(character, worldPosition, out var hasIncompatibleTarget, out var isPveActionForbidden); if (targetObject is null) { // nothing to mine there if (showErrorNotification) { if (isPveActionForbidden) { PveSystem.ClientShowNotificationActionForbidden(); } CannotInteractMessageDisplay.ClientOnCannotInteract( character, hasIncompatibleTarget ? Notification_CannotMineThat : Notification_NothingToMineThere, isOutOfRange: false); } return(false); } if (!SharedIsValidStartLocation(character, worldPosition, out var hasObstacles)) { if (showErrorNotification) { CannotInteractMessageDisplay.ClientOnCannotInteract( character, hasObstacles ? CoreStrings.Notification_ObstaclesOnTheWay : CoreStrings.Notification_TooFar, isOutOfRange: true); } return(false); } ClientDroneStartQueue.Add(itemDrone, worldPosition); return(true); }
private static async Task <string> ClientOnWelcomeMessageReceivedFromServer(Task <string> t) { var message = t.Result; await PveSystem.ClientAwaitPvEModeFromServer(); if (string.IsNullOrWhiteSpace(message)) { // no welcome message if (!Client.CurrentGame.IsConnectedToOfficialServer) { // don't have any welcome message return(string.Empty); } // use official server welcome message message = GetOfficialServerWelcomeMessage(); } else if (Client.CurrentGame.IsConnectedToOfficialServer) { // append official welcome message to the custom welcome message // ReSharper disable once CanExtractXamlLocalizableStringCSharp message += "[br][br]─────────────────────────────────────[br]" + GetOfficialServerWelcomeMessage(); } return(message); }
public static IStaticWorldObject SharedGetCompatibleTarget( ICharacter character, Vector2Ushort worldPosition, out bool hasIncompatibleTarget, out bool isPveActionForbidden) { isPveActionForbidden = false; var tile = IsServer ? Server.World.GetTile(worldPosition) : Client.World.GetTile(worldPosition); var targetObject = tile.StaticObjects.FirstOrDefault(); if (targetObject is null) { hasIncompatibleTarget = false; return(null); } if (!PveSystem.SharedIsAllowStaticObjectDamage(character, targetObject, showClientNotification: false)) { hasIncompatibleTarget = true; isPveActionForbidden = true; return(null); } if (!SharedIsValidDroneTarget(targetObject)) { hasIncompatibleTarget = true; return(null); } hasIncompatibleTarget = false; return(targetObject); }
private static bool SharedHasObstacle( ICharacter character, IStaticWorldObject forStructureRelocation, Vector2D position) { if (PveSystem.SharedIsPve(false)) { return(false); } var characterCenter = character.Position + character.PhysicsBody.CenterOffset; return(TestHasObstacle(position)); // local method for testing if there is an obstacle from current to the specified position bool TestHasObstacle(Vector2D toPosition) { using var obstaclesInTheWay = character.PhysicsBody.PhysicsSpace.TestLine( characterCenter, toPosition, CollisionGroup.Default, sendDebugEvent: false); foreach (var test in obstaclesInTheWay.AsList()) { var testPhysicsBody = test.PhysicsBody; if (testPhysicsBody.AssociatedProtoTile is not null) { // obstacle tile on the way return(true); } var testWorldObject = testPhysicsBody.AssociatedWorldObject; if (testWorldObject is null || ReferenceEquals(testWorldObject, character) || ReferenceEquals(testWorldObject, forStructureRelocation)) { // not an obstacle - it's the character or world object itself continue; } switch (testWorldObject.ProtoWorldObject) { case IProtoObjectDeposit: // allow deposits case ObjectWallDestroyed: // allow destroyed walls continue; } // obstacle object on the way return(true); } // no obstacles return(false); } }
protected override void PrepareTileRequirements(ConstructionTileRequirements tileRequirements) { base.PrepareTileRequirements(tileRequirements); // disallow placing mining bombs on the floor on the PvE servers tileRequirements.Add( new ConstructionTileRequirements.Validator( ConstructionTileRequirements.ErrorNoFreeSpace, c => !PveSystem.SharedIsPve(clientLogErrorIfDataIsNotYetAvailable: false) || c.Tile.StaticObjects.All( o => o.ProtoStaticWorldObject.Kind != StaticObjectKind.Floor))); }
public override bool SharedCanInteract(ICharacter character, IStaticWorldObject worldObject, bool writeToLog) { if (!base.SharedCanInteract(character, worldObject, writeToLog)) { return(false); } var ownerName = GetPublicState(worldObject).OwnerName; if (ownerName == character.Name) { return(true); } if (PveSystem.SharedIsPve(false)) { if (IsClient && PartySystem.ClientIsPartyMember(ownerName) || (IsServer && PartySystem.ServerIsSameParty(Server.Characters.GetPlayerCharacter(ownerName), character))) { // in PvE party members can pickup items of their party members } else { // other players in PvE cannot pickup player's loot if (writeToLog && IsClient) { PveSystem.ClientShowNotificationActionForbidden(); } return(false); } } if (NewbieProtectionSystem.SharedIsNewbie(character)) { // newbie cannot pickup other players' loot if (writeToLog) { NewbieProtectionSystem.SharedShowNewbieCannotDamageOtherPlayersOrLootBags(character, isLootBag: true); } return(false); } // non-newbie character can pickup players' loot // please note this validation has an override for derived ObjectPlayerLootContainerProtected return(true); }
private void RefreshDepletion() { this.ValueCurrent = this.publicState?.StructurePointsCurrent ?? 0; if (this.ValueCurrent <= 0) { if (PveSystem.ClientIsPve(logErrorIfDataIsNotYetAvailable: true)) { // PvE mode doesn't require the deposit under the extractor this.DepletionDurationText = TitleDepositInfinite; this.ValueCurrent = this.ValueMax = 1; } else { this.DepletionDurationText = TitleDepositDepleted; } this.DepletedInPrefixVisibility = Visibility.Collapsed; return; } var lifetimeTotalDurationSeconds = this.protoDeposit.LifetimeTotalDurationSeconds; if (lifetimeTotalDurationSeconds == 0) { this.DepletionDurationText = TitleDepositInfinite; this.DepletedInPrefixVisibility = Visibility.Collapsed; return; } this.DepletedInPrefixVisibility = Visibility.Visible; var structurePointsFraction = this.ValueCurrent / this.ValueMax; var remainsNoExtraction = structurePointsFraction * lifetimeTotalDurationSeconds; var remainsWithExtraction = remainsNoExtraction / this.protoDeposit.DecaySpeedMultiplierWhenExtractingActive; if (remainsNoExtraction == remainsWithExtraction) { // display a single duration this.DepletionDurationText = FormatDuration(remainsNoExtraction); } else { // display an interval this.DepletionDurationText = FormatInterval(remainsWithExtraction, remainsNoExtraction); } }
protected override double SharedCalculateDamageByWeapon( WeaponFinalCache weaponCache, double damagePreMultiplier, IStaticWorldObject targetObject, out double obstacleBlockDamageCoef) { if (!PveSystem.SharedIsAllowStaticObjectDamage(weaponCache.Character, targetObject, showClientNotification: false)) { obstacleBlockDamageCoef = 0; return(0); } if (weaponCache.ProtoWeapon is IProtoItemToolMining protoItemToolMining) { // mining mineral with a mining device // block next damage completely - only one mineral could be mined at once obstacleBlockDamageCoef = 1; // get damage multiplier ("mining speed") var damageMultiplier = weaponCache.CharacterFinalStatsCache.GetMultiplier(StatName.MiningSpeed); return(protoItemToolMining.ServerGetDamageToMineral(targetObject) * damageMultiplier * RateActionMiningSpeedMultiplier.SharedValue); } if (weaponCache.ProtoWeapon is ItemNoWeapon) { // no damage with hands obstacleBlockDamageCoef = 1; if (IsClient) { NotificationSystem.ClientShowNotification(NotificationUsePickaxe, icon: this.Icon); } return(0); } // not a mining tool - call default damage apply method return(base.SharedCalculateDamageByWeapon(weaponCache, damagePreMultiplier, targetObject, out obstacleBlockDamageCoef)); }
protected override double SharedCalculateDamageByWeapon( WeaponFinalCache weaponCache, double damagePreMultiplier, IStaticWorldObject targetObject, out double obstacleBlockDamageCoef) { // ReSharper disable once VariableHidesOuterVariable if (!PveSystem.SharedIsAllowStaticObjectDamage(weaponCache.Character, targetObject, showClientNotification: false)) { obstacleBlockDamageCoef = 0; return(0); } if (weaponCache.ProtoWeapon is IProtoItemToolWoodcutting protoItemToolWoodCutting) { // get damage multiplier ("woodcutting speed") obstacleBlockDamageCoef = 1; var damageMultiplier = weaponCache.CharacterFinalStatsCache.GetMultiplier( StatName.WoodcuttingSpeed); return(protoItemToolWoodCutting.ServerGetDamageToTree(targetObject) * damageMultiplier * ToolsConstants.ActionWoodcuttingSpeedMultiplier * this.SharedGetDamageMultiplierByGrowthProgress(targetObject)); } if (weaponCache.ProtoWeapon is ItemNoWeapon) { // no damage with hands obstacleBlockDamageCoef = 1; if (IsClient) { NotificationSystem.ClientShowNotification(NotificationUseAxe, icon: this.Icon); } return(0); } // not a wood-cutting tool - call default damage apply method return(base.SharedCalculateDamageByWeapon(weaponCache, damagePreMultiplier, targetObject, out obstacleBlockDamageCoef)); }
public static async void ClientPutCurrentVehicle() { if (!PveSystem.ClientIsPve(logErrorIfDataIsNotYetAvailable: true)) { // this feature is available only in PvE return; } var vehicleAssemblyBay = InteractionCheckerSystem.SharedGetCurrentInteraction( ClientCurrentCharacterHelper.Character); var isSuccess = await Instance.CallServer(_ => _.ServerRemote_PutCurrentVehicle()); if (isSuccess) { Client.Audio.PlayOneShot(SoundResourcePutVehicle, vehicleAssemblyBay); } }
public double Get(TechTier tier, bool isSpecialized) { if (PveSystem.SharedIsPve(false)) { return(0); } var tierIndex = (int)tier - (int)TechConstants.PvPTimeGateStartsFromTier; if (tierIndex < 0) { return(0); } var index = 2 * tierIndex + (isSpecialized ? 1 : 0); return(this.TimeGates[index]); }
private bool ValidateTarget(IStaticWorldObject targetObject, out Vector2D targetPoint) { targetPoint = Vector2D.Zero; if (!PveSystem.SharedIsAllowStaticObjectDamage(CurrentCharacter, targetObject, showClientNotification: false)) { return(false); } var shape = targetObject.PhysicsBody.Shapes.FirstOrDefault(s => s.CollisionGroup == CollisionGroups.HitboxMelee); if (shape == null) { Api.Logger.Error("Automaton: target object has no HitBoxMelee shape " + targetObject); return(false); } targetPoint = ShapeCenter(shape) + targetObject.PhysicsBody.Position; return(AdditionalValidation(targetObject) && CheckForObstacles(targetObject, targetPoint)); }
protected override double SharedCalculateDamageByWeapon( WeaponFinalCache weaponCache, double damagePreMultiplier, IStaticWorldObject targetObject, out double obstacleBlockDamageCoef) { obstacleBlockDamageCoef = 1; if (!PveSystem.SharedIsAllowStructureDamage(weaponCache.Character, targetObject, showClientNotification: false)) { return(0); } if (weaponCache.ProtoWeapon is IProtoItemToolWoodcutting protoItemToolWoodCutting) { // get damage multiplier ("woodcutting speed") var damageMultiplier = weaponCache.Character .SharedGetFinalStatMultiplier(StatName.WoodcuttingSpeed); return(protoItemToolWoodCutting.DamageToTree * damageMultiplier * ToolsConstants.ActionWoodcuttingSpeedMultiplier); } if (weaponCache.ProtoWeapon is ItemNoWeapon) { // no damage with hands if (IsClient) { NotificationSystem.ClientShowNotification(NotificationUseAxe, icon: this.Icon); } return(0); } // not a wood-cutting tool - call default damage apply method return(base.SharedCalculateDamageByWeapon(weaponCache, damagePreMultiplier, targetObject, out obstacleBlockDamageCoef)); }
public virtual bool SharedCanInteract(ICharacter character, TWorldObject worldObject, bool writeToLog) { if (!this.IsInteractableObject) { return(false); } if (character.GetPublicState <ICharacterPublicState>().IsDead || IsServer && !character.ServerIsOnline) { return(false); } if (worldObject is IStaticWorldObject staticWorldObject) { if (PveSystem.SharedIsPve(clientLogErrorIfDataIsNotYetAvailable: false)) { if (!PveSystem.SharedValidateInteractionIsNotForbidden(character, staticWorldObject, writeToLog)) { // action forbidden by PvE system return(false); } } else // PvP servers have newbie protection system { if (!NewbieProtectionSystem.SharedValidateInteractionIsNotForbidden( character, staticWorldObject, writeToLog)) { // action forbidden by newbie protection system return(false); } } } return(this.SharedIsInsideCharacterInteractionArea(character, worldObject, writeToLog)); }
protected override double SharedCalculateDamageByWeapon( WeaponFinalCache weaponCache, double damagePreMultiplier, IDynamicWorldObject targetObject, out double obstacleBlockDamageCoef) { if (PveSystem.SharedIsPve(false) && (weaponCache.Character is null || !weaponCache.Character.IsNpc)) { // no PvP damage in PvE (only creature damage is allowed) obstacleBlockDamageCoef = 0; return(0); } return(base.SharedCalculateDamageByWeapon(weaponCache, damagePreMultiplier, targetObject, out obstacleBlockDamageCoef)); }
private static double SharedGetSpeedMultiplier(IStaticWorldObject objectDeposit) { return(GetDepositExtractionRateMultiplier() * StructureConstants.DepositsExtractionSpeedMultiplier); double GetDepositExtractionRateMultiplier() { if (PveSystem.SharedIsPve(clientLogErrorIfDataIsNotYetAvailable: false)) { return(ExtractorPvE); } // extractor in PvP if (objectDeposit is null) { return(ExtractorPvpWithoutDeposit); } var protoObjectDeposit = (IProtoObjectDeposit)objectDeposit.ProtoStaticWorldObject; return(ExtractorPvpWithDeposit); } }
public static bool SharedIsAllowInteraction( ICharacter character, IStaticWorldObject worldObject, bool showClientNotification) { if (!SharedIsEnabled) { return(true); } if (character is null || !PveSystem.SharedIsPve(clientLogErrorIfDataIsNotYetAvailable: false)) { return(true); } var worldObjectPublicState = worldObject.AbstractPublicState as IWorldObjectPublicStateWithClaim; if (worldObjectPublicState?.WorldObjectClaim is null || WorldObjectClaim.SharedIsClaimedForPlayer(worldObjectPublicState.WorldObjectClaim, character)) { return(true); } if (IsClient && showClientNotification) { NotificationSystem.ClientShowNotification( CoreStrings.WorldObjectClaim_Title, CoreStrings.WorldObjectClaim_Description + "[br]" + CoreStrings.WorldObjectClaim_Description2, NotificationColor.Bad, icon: TextureResourceClaimedObject); } return(false); }
private static async Task <WelcomeMessageRemoteData> ClientOnWelcomeMessageReceivedFromServer( Task <WelcomeMessageRemoteData> t) { const string h1s = "[h1]", h1c = "[/h1]", br = "[br]", brbr = br + br; var data = t.Result; var sb = new StringBuilder(); var welcomeMessage = data.WelcomeMessage; await PveSystem.ClientAwaitPvEModeFromServer(); // Header 0 - Community server if (!Client.CurrentGame.IsConnectedToOfficialServer) { sb.AppendLine(h1s + HeaderDescriptionCommunity + h1c) .AppendLine(br) .AppendLine(DescriptionCommunity) .AppendLine(brbr); } // Header 1 - Server welcome message (section displayed only when available) if (!string.IsNullOrWhiteSpace(welcomeMessage)) { sb.AppendLine(h1s + HeaderWelcomeMessage + h1c) .AppendLine(br) .AppendLine(welcomeMessage) .AppendLine(brbr); } // Header 2 - PvP or PvE server description var isPveServer = PveSystem.ClientIsPve(logErrorIfDataIsNotYetAvailable: true); if (isPveServer) { sb.AppendLine(h1s + HeaderDescriptionPvE + h1c) .AppendLine(br) .AppendLine(DescriptionPvE) .AppendLine(brbr); } else // PvP server { sb.AppendLine(h1s + HeaderDescriptionPvP + h1c) .AppendLine(br) .AppendLine(DescriptionPvP) .AppendLine(brbr); } // Header 3 - Modded server (if the server is modded) if (Client.CurrentGame.ServerInfo.ModsOnServer.Count > 0) { sb.AppendLine(h1s + HeaderModded + h1c) .AppendLine(br) .AppendLine(DescriptionModded) .AppendLine(brbr); } // Header 4 - Server wipe information sb.AppendLine(h1s + HeaderWipeInformation + h1c) .AppendLine(br); if (data.ScheduledWipeDateUtc.HasValue) { sb.AppendLine(string.Format(CoreStrings.ServerWipeInfoStartedDate_Format, FormatDate(Client.CurrentGame.ServerInfo.CreationDateUtc.ToLocalTime()))) .AppendLine(br) .AppendLine(string.Format(CoreStrings.ServerWipeInfoNextWipeDate_Format, FormatDate(data.ScheduledWipeDateUtc.Value.ToLocalTime()))) .AppendLine(br) .AppendLine(ClientLocalTimeZoneHelper.GetTextTimeAlreadyConvertedToLocalTimeZone()); } else if (Client.CurrentGame.IsConnectedToOfficialServer && isPveServer) { sb.AppendLine(CoreStrings.ServerWipeInfoOnMajorUpdatesOnly); } else { sb.AppendLine(CoreStrings.ServerWipeInfoNotSpecified + " " + CoreStrings.ConsultServerAdministrator); } return(new WelcomeMessageRemoteData(sb.ToString(), data.ScheduledWipeDateUtc)); }
private async static void InitPVE() { await PveSystem.ClientAwaitPvEModeFromServer(); IsPVE = PveSystem.ClientIsPve(false); }
private static void SharedRebuildAllNodes() { if (Api.IsClient && !PveSystem.ClientIsPveFlagReceived) { AvailableTechGroups = new TechGroup[0]; return; } var isPvE = PveSystem.SharedIsPve(false); var allTechGroups = Api.FindProtoEntities <TechGroup>(); foreach (var techGroup in allTechGroups) { SetupTimeGate(techGroup); techGroup.Nodes = techGroup.IsAvailable ? (IReadOnlyList <TechNode>)LazyAllNodesWithoutFiltering .Value .Where(n => n.Group == techGroup && n.IsAvailable) .OrderBy(n => n.HierarchyLevel) .ThenBy(n => n.Order) .ThenBy(n => n.ShortId) .ToList() : new TechNode[0]; var rootNodes = new List <TechNode>(); foreach (var node in techGroup.Nodes) { node.SharedResetCachedLearningPointsPrice(); if (node.IsRootNode) { rootNodes.Add(node); } } techGroup.RootNodes = rootNodes; var requirements = new Requirements(); techGroup.PrepareTechGroup(requirements); if (techGroup.LearningPointsPrice > 0) { requirements.Insert(0, new TechGroupRequirementLearningPoints(techGroup.LearningPointsPrice)); } if (!PveSystem.SharedIsPve(clientLogErrorIfDataIsNotYetAvailable: false) && techGroup.TimeGatePvP > 0) { requirements.Add(new TechGroupRequirementTimeGate(techGroup.TimeGatePvP)); } techGroup.GroupRequirements = requirements; Api.SafeInvoke(techGroup.NodesChanged); } AvailableTechGroups = allTechGroups.Where(t => t.IsAvailable).ToArray(); Api.SafeInvoke(AvailableTechGroupsChanged); void SetupTimeGate(TechGroup techGroup) { if (isPvE) { techGroup.TimeGatePvP = 0; return; } if (techGroup.IsPrimary) { techGroup.TimeGatePvP = techGroup.Tier switch { TechTier.Tier3 => TechConstants.PvpTechTimeGameTier3Basic, TechTier.Tier4 => TechConstants.PvpTechTimeGameTier4Basic, TechTier.Tier5 => TechConstants.PvpTechTimeGameTier5Basic, _ => 0 }; } else { techGroup.TimeGatePvP = techGroup.Tier switch { TechTier.Tier3 => TechConstants.PvpTechTimeGameTier3Specialized, TechTier.Tier4 => TechConstants.PvpTechTimeGameTier4Specialized, TechTier.Tier5 => TechConstants.PvpTechTimeGameTier5Specialized, _ => 0 }; } } }
public virtual bool SharedCanInteract(ICharacter character, TWorldObject worldObject, bool writeToLog) { if (!this.IsInteractableObject) { return(false); } try { this.VerifyGameObject(worldObject); } catch (Exception ex) { Logger.Warning($"Interaction check failed: {ex.GetType().FullName}: {ex.Message}"); return(false); } if (character.GetPublicState <ICharacterPublicState>().IsDead || IsServer && !character.ServerIsOnline || ((IsServer || character.IsCurrentClientCharacter) && PlayerCharacter.GetPrivateState(character).IsDespawned)) { if (writeToLog) { Logger.Warning( $"Character cannot interact with {worldObject} - character is offline/despawned/dead.", character); } return(false); } if (worldObject is IStaticWorldObject staticWorldObject) { if (PveSystem.SharedIsPve(clientLogErrorIfDataIsNotYetAvailable: false)) { if (!PveSystem.SharedValidateInteractionIsNotForbidden(character, staticWorldObject, writeToLog)) { // action forbidden by PvE system return(false); } } else // PvP servers have newbie protection system { if (!NewbieProtectionSystem.SharedValidateInteractionIsNotForbidden( character, staticWorldObject, writeToLog)) { // action forbidden by newbie protection system return(false); } } } return(this.SharedIsInsideCharacterInteractionArea(character, worldObject, writeToLog)); }
public virtual bool SharedOnDamage( WeaponFinalCache weaponCache, IStaticWorldObject targetObject, double damagePreMultiplier, out double obstacleBlockDamageCoef, out double damageApplied) { var objectPublicState = GetPublicState(targetObject); var previousStructurePoints = objectPublicState.StructurePointsCurrent; if (previousStructurePoints <= 0f) { // already destroyed static world object obstacleBlockDamageCoef = 0; damageApplied = 0; return(false); } var serverDamage = this.SharedCalculateDamageByWeapon( weaponCache, damagePreMultiplier, targetObject, out obstacleBlockDamageCoef); if (serverDamage < 0) { Logger.Warning( $"Server damage less than 0 and this is suspicious. {this} calculated damage: {serverDamage:0.###}"); serverDamage = 0; } if (IsClient) { // simply call these methods to display a client notification only! // they are not used for anything else here // to calculate damage they're used in WeaponDamageSystem.ServerCalculateTotalDamage method. RaidingProtectionSystem.SharedCanRaid(targetObject, showClientNotification: true); LandClaimShieldProtectionSystem.SharedCanRaid(targetObject, showClientNotification: true); PveSystem.SharedIsAllowStaticObjectDamage(weaponCache.Character, targetObject, showClientNotification: true); NewbieProtectionSystem.SharedIsAllowStructureDamage(weaponCache.Character, targetObject, showClientNotification: true); damageApplied = 0; return(true); } if (serverDamage <= 0) { // no damage applied damageApplied = 0; return(true); } // apply damage damageApplied = serverDamage; var newStructurePoints = (float)(previousStructurePoints - serverDamage); if (newStructurePoints < 0) { newStructurePoints = 0; } Logger.Info( $"Damage applied to {targetObject} by {weaponCache.Character}:\n{serverDamage} dmg, current structure points {newStructurePoints}/{this.StructurePointsMax}, {weaponCache.Weapon}"); objectPublicState.StructurePointsCurrent = newStructurePoints; try { this.ServerOnStaticObjectDamageApplied( weaponCache, targetObject, previousStructurePoints, newStructurePoints); } catch (Exception ex) { Logger.Exception(ex, $"Problem on processing {nameof(this.ServerOnStaticObjectDamageApplied)}()"); } if (newStructurePoints <= 0f) { this.ServerOnStaticObjectZeroStructurePoints(weaponCache, weaponCache.Character, targetObject); } return(true); }
public override bool SharedCanInteract(ICharacter character, IStaticWorldObject worldObject, bool writeToLog) { return(PveSystem.SharedValidateInteractionIsNotForbidden(character, worldObject, writeToLog) && NewbieProtectionSystem.SharedValidateInteractionIsNotForbidden(character, worldObject, writeToLog) && this.SharedIsInsideCharacterInteractionArea(character, worldObject, writeToLog)); }
public static double ServerCalculateTotalDamage( WeaponFinalCache weaponCache, IWorldObject targetObject, FinalStatsCache targetFinalStatsCache, double damagePreMultiplier, bool clampDefenseTo1) { if (targetObject is IStaticWorldObject staticWorldObject && (!RaidingProtectionSystem.SharedCanRaid(staticWorldObject, showClientNotification: false) || !PveSystem.SharedIsAllowStructureDamage(weaponCache.Character, staticWorldObject, showClientNotification: false))) { return(0); } if (weaponCache.ProtoObjectExplosive != null && targetObject.ProtoWorldObject is IProtoStaticWorldObject targetStaticWorldObjectProto) { // special case - apply the explosive damage return(ServerCalculateTotalDamageByExplosive(weaponCache.ProtoObjectExplosive, targetStaticWorldObjectProto, damagePreMultiplier)); } // these two cases apply only if damage dealt not by a bomb if (ServerIsRestrictedPvPDamage(weaponCache, targetObject, out var isPvPcase, out var isFriendlyFireCase)) { return(0); } var damageValue = damagePreMultiplier * weaponCache.DamageValue; var invertedArmorPiercingCoef = weaponCache.InvertedArmorPiercingCoef; var totalDamage = 0d; // calculate total damage by summing all the damage components foreach (var damageDistribution in weaponCache.DamageDistributions) { var defenseStatName = SharedGetDefenseStatName(damageDistribution.DamageType); var defenseFraction = targetFinalStatsCache[defenseStatName]; defenseFraction = MathHelper.Clamp(defenseFraction, 0, clampDefenseTo1 ? 1 : double.MaxValue); totalDamage += ServerCalculateDamageComponent( damageValue, invertedArmorPiercingCoef, damageDistribution, defenseFraction); } // multiply on final multiplier (usually used for expanding projectiles) totalDamage *= weaponCache.FinalDamageMultiplier; var damagingCharacter = weaponCache.Character; if (isPvPcase) { // apply PvP damage multiplier totalDamage *= WeaponConstants.DamagePvpMultiplier; } else if (damagingCharacter != null && !damagingCharacter.IsNpc) { // apply PvE damage multiplier totalDamage *= WeaponConstants.DamagePveMultiplier; } else if (damagingCharacter != null && damagingCharacter.IsNpc) { // apply creature damage multiplier totalDamage *= WeaponConstants.DamageCreaturesMultiplier; if (targetObject is ICharacter victim && !victim.ServerIsOnline && !victim.IsNpc) { // don't deal creature damage to offline players totalDamage = 0; } } if (isFriendlyFireCase) { totalDamage *= WeaponConstants.DamageFriendlyFireMultiplier; } return(totalDamage); }
public static void ClientStartRelocation(IStaticWorldObject objectStructure) { var protoStructure = objectStructure.ProtoStaticWorldObject; var character = Client.Characters.CurrentPlayerCharacter; if (IsInObjectPlacementMode || ConstructionPlacementSystem.IsInObjectPlacementMode) { // already relocating/placing something return; } if (!SharedIsRelocatable(objectStructure)) { return; } if (!CreativeModeSystem.SharedIsInCreativeMode(character) && !LandClaimSystem.SharedIsOwnedLand(objectStructure.TilePosition, character, requireFactionPermission: true, out var hasNoFactionPermission, out _)) { // the building location or destination is in an area that is not owned by the player SharedShowCannotRelocateNotification( character, protoStructure, hasNoFactionPermission); return; } var isPvEorWithinInteractionArea = PveSystem.SharedIsPve(false) || protoStructure.SharedIsInsideCharacterInteractionArea( Api.Client.Characters.CurrentPlayerCharacter, objectStructure, writeToLog: false); if (!isPvEorWithinInteractionArea) { CannotInteractMessageDisplay.ClientOnCannotInteract( character, CoreStrings.Notification_TooFar, isOutOfRange: true); return; } if (LandClaimSystem.SharedIsUnderRaidBlock(character, objectStructure)) { // the building is in an area under the raid ConstructionSystem.SharedShowCannotBuildNotification( character, LandClaimSystem.ErrorRaidBlockActionRestricted_Message, protoStructure); return; } if (LandClaimShieldProtectionSystem.SharedIsUnderShieldProtection(objectStructure)) { // the building is in an area under shield protection LandClaimShieldProtectionSystem.SharedSendNotificationActionForbiddenUnderShieldProtection( character); return; } ClientDisableConstructionRelocation(); var sceneObject = Client.Scene.CreateSceneObject("StructureRelocationHelper"); componentObjectPlacementHelper = sceneObject.AddComponent <ClientComponentObjectPlacementHelper>(); componentRelocationHelper = sceneObject.AddComponent <ClientComponentObjectRelocationHelper>(); componentObjectPlacementHelper .Setup(protoStructure, isCancelable: true, isRepeatCallbackIfHeld: false, isDrawConstructionGrid: true, isBlockingInput: true, validateCanPlaceCallback: ClientValidateCanRelocate, placeSelectedCallback: ClientConstructionPlaceSelectedCallback, delayRemainsSeconds: 0.1); componentObjectPlacementHelper.HideBlueprintOnOverlapWithTheSameObject = false; componentRelocationHelper.Setup(objectStructure); void ClientValidateCanRelocate( Vector2Ushort tilePosition, bool logErrors, out string errorMessage, out bool canPlace, out bool isTooFar) { if (tilePosition == objectStructure.TilePosition) { canPlace = true; isTooFar = false; errorMessage = null; return; } if (!SharedCheckTileRequirementsForRelocation(character, objectStructure, tilePosition, out errorMessage, logErrors: logErrors)) { // time requirements are not valid canPlace = false; isTooFar = false; return; } if (!SharedValidateCanCharacterRelocateStructure(character, objectStructure, tilePosition, out errorMessage, logErrors: logErrors)) { canPlace = true; isTooFar = true; return; } if (SharedHasObstacle( character, objectStructure, tilePosition.ToVector2D() + protoStructure.Layout.Center)) { if (logErrors) { CannotInteractMessageDisplay.ClientOnCannotInteract( character, CoreStrings.Notification_ObstaclesOnTheWay, isOutOfRange: true); } errorMessage = CoreStrings.Notification_ObstaclesOnTheWay; canPlace = true; isTooFar = true; return; } canPlace = true; isTooFar = false; } void ClientConstructionPlaceSelectedCallback(Vector2Ushort tilePosition) { if (SharedHasObstacle( character, objectStructure, tilePosition.ToVector2D() + protoStructure.Layout.Center)) { CannotInteractMessageDisplay.ClientOnCannotInteract( character, CoreStrings.Notification_ObstaclesOnTheWay, isOutOfRange: true); return; } ClientTimersSystem.AddAction(0.1, ClientDisableConstructionRelocation); if (tilePosition != objectStructure.TilePosition) { Instance.CallServer(_ => _.ServerRemote_RelocateStructure(objectStructure, tilePosition)); } } }
protected override bool ClientItemUseFinish(ClientItemData data) { var character = ClientCurrentCharacterHelper.Character; var characterTilePosition = character.TilePosition; var mouseTilePosition = Client.Input.MousePointedTilePosition; var dronesNumberToLaunch = Api.Client.Input.IsKeyHeld(InputKey.Shift, evenIfHandled: true) ? this.MaxDronesToControl : 1; using var tempExceptDrones = Api.Shared.GetTempList <IItem>(); using var tempExceptTargets = Api.Shared.GetTempList <Vector2Ushort>(); for (var index = 0; index < dronesNumberToLaunch; index++) { var showErrorNotification = index == 0; var itemDrone = CharacterDroneControlSystem.ClientSelectNextDrone(tempExceptDrones.AsList()); if (itemDrone is null) { if (CharacterDroneControlSystem.SharedIsMaxDronesToControlNumberExceeded( character, clientShowErrorNotification: showErrorNotification)) { break; } if (showErrorNotification) { CannotInteractMessageDisplay.ClientOnCannotInteract( character, CharacterDroneControlSystem.Notification_ErrorNoDrones_Title, isOutOfRange: false); } break; } tempExceptDrones.Add(itemDrone); Vector2Ushort targetPosition; if (index == 0) { targetPosition = mouseTilePosition; var targetObject = CharacterDroneControlSystem .SharedGetCompatibleTarget(character, mouseTilePosition, out var hasIncompatibleTarget, out var isPveActionForbidden); if (targetObject is null) { if (showErrorNotification) { if (isPveActionForbidden) { PveSystem.ClientShowNotificationActionForbidden(); } CannotInteractMessageDisplay.ClientOnCannotInteract( character, hasIncompatibleTarget ? CharacterDroneControlSystem.Notification_CannotMineThat : CharacterDroneControlSystem.Notification_NothingToMineThere, isOutOfRange: false); } return(false); } if (!WorldObjectClaimSystem.SharedIsAllowInteraction(character, targetObject, showClientNotification: showErrorNotification)) { return(false); } if (CharacterDroneControlSystem.SharedIsTargetAlreadyScheduledForAnyActiveDrone( character, mouseTilePosition, logError: false)) { // already scheduled a drone mining there...try find another target of the same type targetPosition = TryGetNextTargetPosition(); if (targetPosition == default) { // no further targets CannotInteractMessageDisplay.ClientOnCannotInteract( character, CharacterDroneControlSystem.Notification_DroneAlreadySent, isOutOfRange: false); return(false); } } } else { targetPosition = TryGetNextTargetPosition(); if (targetPosition == default) { // no further targets break; } } if (!CharacterDroneControlSystem.ClientTryStartDrone(itemDrone, targetPosition, showErrorNotification: showErrorNotification)) { break; } tempExceptTargets.Add(targetPosition); } // always return false as we don't want to play any device sounds return(false); Vector2Ushort TryGetNextTargetPosition() { var targetObjectProto = CharacterDroneControlSystem .SharedGetCompatibleTarget(character, mouseTilePosition, out _, out _)? .ProtoWorldObject; if (targetObjectProto is null) { return(default);
public static void SetupSkeletonEquipmentForCharacter( ICharacter character, IItemsContainer containerEquipment, IComponentSkeleton skeletonRenderer, ProtoCharacterSkeleton skeleton, List <IClientComponent> skeletonComponents, bool isPreview = false) { if (!(skeleton is SkeletonHumanMale) && !(skeleton is SkeletonHumanFemale)) { // not a human // setup only implants using var equipmentImplants = Api.Shared.WrapInTempList( containerEquipment.GetItemsOfProto <IProtoItemEquipmentImplant>()); foreach (var item in equipmentImplants.AsList()) { var proto = (IProtoItemEquipmentImplant)item.ProtoGameObject; proto.ClientSetupSkeleton(item, character, skeletonRenderer, skeletonComponents, isPreview); } return; } bool isMale, isHeadEquipmentHiddenForSelfAndPartyMembers; CharacterHumanFaceStyle faceStyle; if (character.ProtoCharacter is PlayerCharacter) { var publicState = PlayerCharacter.GetPublicState(character); faceStyle = publicState.FaceStyle; isMale = publicState.IsMale; isHeadEquipmentHiddenForSelfAndPartyMembers = publicState.IsHeadEquipmentHiddenForSelfAndPartyMembers; if (isMale && !(skeleton is SkeletonHumanMale) || !isMale && !(skeleton is SkeletonHumanFemale)) { throw new Exception( $"Skeleton don\'t match the gender of the player\'s character: isMale={isMale}, {skeleton}"); } } else { // for NPC it will generate a random face isMale = true; faceStyle = SharedCharacterFaceStylesProvider.GetForGender(isMale).GenerateRandomFace(); isHeadEquipmentHiddenForSelfAndPartyMembers = false; } skeletonRenderer.ResetAttachments(); skeleton.ClientResetItemInHand(skeletonRenderer); var skinToneId = faceStyle.SkinToneId; if (string.IsNullOrEmpty(skinToneId)) { skeletonRenderer.DefaultTextureRemapper = null; } else { // use colorizer for the original sprites (to apply the skin tone) skeletonRenderer.DefaultTextureRemapper = textureResource => { var filePath = textureResource.LocalPath; if (filePath.IndexOf("/Weapon", StringComparison.Ordinal) >= 0 || filePath.IndexOf("/Head", StringComparison.Ordinal) >= 0) { // no need to remap the original head and weapon sprites // (they're never used as is) return(textureResource); } return(ClientCharacterSkinTexturesCache.Get(textureResource, skinToneId)); }; } // setup equipment items using var equipmentItems = Api.Shared.WrapInTempList( containerEquipment.GetItemsOfProto <IProtoItemEquipment>()); if (!IsAllowNakedHumans) { if (!equipmentItems.AsList().Any(i => i.ProtoGameObject is IProtoItemEquipmentArmor)) { // no armor equipped - apply generic one var pants = GenericPantsAttachments.Value; ClientSkeletonAttachmentsLoader.SetAttachments( skeletonRenderer, isMale ? pants.SlotAttachmentsMale : pants.SlotAttachmentsFemale); // select a random generic T-shirt based on character ID var allShirts = GenericShirtAttachments.Value; var selectedShirtIndex = character.Id % allShirts.Length; var shirt = allShirts[(int)selectedShirtIndex]; ClientSkeletonAttachmentsLoader.SetAttachments( skeletonRenderer, isMale ? shirt.SlotAttachmentsMale : shirt.SlotAttachmentsFemale); } } IItem headEquipmentForFaceSprite = null; foreach (var item in equipmentItems.AsList()) { var proto = (IProtoItemEquipment)item.ProtoGameObject; proto.ClientSetupSkeleton(item, character, skeletonRenderer, skeletonComponents, isPreview); if (item.ProtoItem is IProtoItemEquipmentHead && headEquipmentForFaceSprite is null) { headEquipmentForFaceSprite = item; } } if (isHeadEquipmentHiddenForSelfAndPartyMembers && (character.IsCurrentClientCharacter || PartySystem.ClientIsPartyMember(character.Name) || PveSystem.ClientIsPve(false))) { headEquipmentForFaceSprite = null; } // generate head sprites for human players const string slotName = "Head", attachmentName = "Head"; headGenerationId++; var spriteQualityOffset = skeletonRenderer.SpriteQualityOffset; skeletonRenderer.SetAttachmentSprite( skeleton.SkeletonResourceFront, slotName, attachmentName, new ProceduralTexture( $"Head Front CharacterID={character.Id} gen={headGenerationId}", proceduralTextureRequest => ClientCharacterHeadSpriteComposer.GenerateHeadSprite( new CharacterHeadSpriteData(faceStyle, headEquipmentForFaceSprite, skeleton.SkeletonResourceFront), proceduralTextureRequest, isMale, headSpriteType: ClientCharacterHeadSpriteComposer.HeadSpriteType.Front, spriteQualityOffset: spriteQualityOffset), isTransparent: true, isUseCache: false)); skeletonRenderer.SetAttachmentSprite( skeleton.SkeletonResourceBack, slotName, attachmentName, new ProceduralTexture( $"Head Back CharacterID={character.Id} gen={headGenerationId}", proceduralTextureRequest => ClientCharacterHeadSpriteComposer.GenerateHeadSprite( new CharacterHeadSpriteData(faceStyle, headEquipmentForFaceSprite, skeleton.SkeletonResourceBack), proceduralTextureRequest, isMale, headSpriteType: ClientCharacterHeadSpriteComposer.HeadSpriteType.Back, spriteQualityOffset: spriteQualityOffset), isTransparent: true, isUseCache: false)); skeletonRenderer.SetAttachmentSprite( skeleton.SkeletonResourceBack, slotName + "Back", attachmentName, new ProceduralTexture( $"Head Back2 CharacterID={character.Id} gen={headGenerationId}", proceduralTextureRequest => ClientCharacterHeadSpriteComposer.GenerateHeadSprite( new CharacterHeadSpriteData(faceStyle, headEquipmentForFaceSprite, skeleton.SkeletonResourceBack), proceduralTextureRequest, isMale, headSpriteType: ClientCharacterHeadSpriteComposer.HeadSpriteType.BackOverlay, spriteQualityOffset: spriteQualityOffset), isTransparent: true, isUseCache: false)); }
public static double ServerCalculateTotalDamage( WeaponFinalCache weaponCache, IWorldObject targetObject, FinalStatsCache targetFinalStatsCache, double damagePreMultiplier, bool clampDefenseTo1) { if (targetObject is IStaticWorldObject staticWorldObject && (!RaidingProtectionSystem.SharedCanRaid(staticWorldObject, showClientNotification: false) || !LandClaimShieldProtectionSystem.SharedCanRaid(staticWorldObject, showClientNotification: false) || !PveSystem.SharedIsAllowStaticObjectDamage(weaponCache.Character, staticWorldObject, showClientNotification: false) || !NewbieProtectionSystem.SharedIsAllowStructureDamage(weaponCache.Character, staticWorldObject, showClientNotification: false))) { return(0); } if (targetObject.ProtoGameObject is IProtoVehicle && !PveSystem.SharedIsAllowVehicleDamage(weaponCache, (IDynamicWorldObject)targetObject, showClientNotification: false)) { return(0); } if (weaponCache.ProtoExplosive is not null && targetObject is IStaticWorldObject targetStaticWorldObject) { // special case - apply the explosive damage to static object return(ServerCalculateTotalDamageByExplosive(weaponCache.Character, weaponCache.ProtoExplosive, targetStaticWorldObject, damagePreMultiplier)); } if (ServerIsRestrictedPvPDamage(weaponCache, targetObject, out var isPvPcase, out var isFriendlyFireCase)) { return(0); } var damageValue = damagePreMultiplier * weaponCache.DamageValue; var invertedArmorPiercingCoef = weaponCache.InvertedArmorPiercingCoef; var totalDamage = 0d; // calculate total damage by summing all the damage components foreach (var damageDistribution in weaponCache.DamageDistributions) { var defenseStatName = SharedGetDefenseStatName(damageDistribution.DamageType); var defenseFraction = targetFinalStatsCache[defenseStatName]; defenseFraction = MathHelper.Clamp(defenseFraction, 0, clampDefenseTo1 ? 1 : double.MaxValue); totalDamage += ServerCalculateDamageComponent( damageValue, invertedArmorPiercingCoef, damageDistribution, defenseFraction); } // multiply on final multiplier (usually used for expanding projectiles) totalDamage *= weaponCache.FinalDamageMultiplier; var damagingCharacter = weaponCache.Character; if (isPvPcase) { // apply PvP damage multiplier totalDamage *= WeaponConstants.DamagePvpMultiplier; } else if (damagingCharacter is not null && !damagingCharacter.IsNpc && targetObject.ProtoGameObject is IProtoCharacterMob protoCharacterMob && !protoCharacterMob.IsBoss) { // apply PvE damage multiplier totalDamage *= WeaponConstants.DamagePveMultiplier; }
public static bool SharedValidateCanCharacterRelocateStructure( ICharacter character, IStaticWorldObject objectStructure, Vector2Ushort toPosition, out string errorMessage, bool logErrors) { if (!SharedIsRelocatable(objectStructure)) { errorMessage = null; return(false); } if (!SharedCheckTileRequirementsForRelocation(character, objectStructure, toPosition, out errorMessage, logErrors)) { return(false); } if (!(objectStructure.ProtoGameObject is IProtoObjectStructure protoStructure)) { return(false); } var maxRelocationDistance = PveSystem.SharedIsPve(true) ? MaxRelocationDistancePvE : MaxRelocationDistancePvP; if (objectStructure.TilePosition.TileSqrDistanceTo(toPosition) > maxRelocationDistance * maxRelocationDistance) { if (logErrors) { ConstructionSystem.SharedShowCannotPlaceNotification( character, CoreStrings.Notification_TooFar, protoStructure); } errorMessage = CoreStrings.Notification_TooFar; return(false); } var itemInHands = character.SharedGetPlayerSelectedHotbarItem(); if (!(itemInHands.ProtoGameObject is IProtoItemToolToolbox)) { return(false); } if (CreativeModeSystem.SharedIsInCreativeMode(character)) { errorMessage = null; return(true); } if (!LandClaimSystem.SharedIsOwnedLand(objectStructure.TilePosition, character, requireFactionPermission: true, out var hasNoFactionPermission, ownedArea: out _) || !IsOwnedLand(toPosition, out hasNoFactionPermission)) { errorMessage = string.Format(CoreStrings.Faction_Permission_Required_Format, CoreStrings.Faction_Permission_LandClaimManagement_Title); // the building location or destination is in an area that is not owned by the player if (logErrors) { SharedShowCannotRelocateNotification( character, protoStructure, hasNoFactionPermission); } return(false); } if (LandClaimSystem.SharedIsUnderRaidBlock(character, objectStructure)) { // the building is in an area under the raid errorMessage = LandClaimSystem.ErrorRaidBlockActionRestricted_Message; if (logErrors) { ConstructionSystem.SharedShowCannotPlaceNotification( character, LandClaimSystem.ErrorRaidBlockActionRestricted_Message, protoStructure); } return(false); } if (LandClaimShieldProtectionSystem.SharedIsUnderShieldProtection(objectStructure)) { // the building is in an area under shield protection errorMessage = CoreStrings.ShieldProtection_ActionRestrictedBaseUnderShieldProtection; if (logErrors) { LandClaimShieldProtectionSystem.SharedSendNotificationActionForbiddenUnderShieldProtection( character); } return(false); } errorMessage = null; return(true); bool IsOwnedLand( Vector2Ushort startTilePosition, out bool hasNoFactionPermission) { var worldObjectLayoutTileOffsets = objectStructure.ProtoStaticWorldObject.Layout.TileOffsets; foreach (var tileOffset in worldObjectLayoutTileOffsets) { if (!LandClaimSystem.SharedIsOwnedLand(startTilePosition.AddAndClamp(tileOffset), character, requireFactionPermission: true, out hasNoFactionPermission, out _)) { return(false); } } hasNoFactionPermission = false; return(true); } }