예제 #1
0
 public static uint SharedCalculateTotalEnergyCharge(IDynamicWorldObject vehicle)
 {
     using var tempItemsList = SharedGetTempListFuelItemsForVehicle(vehicle);
     return(SharedCalculateTotalEnergyCharge(tempItemsList));
 }
예제 #2
0
        private static void ServerCharacterExitVehicleNow(ICharacter character, IDynamicWorldObject vehicle)
        {
            if (ServerVehicleQuitRequests.TryGetValue(character, out var requestedVehicleToQuit))
            {
                ServerVehicleQuitRequests.Remove(character);
                requestedVehicleToQuit.GetPublicState <VehiclePublicState>()
                .IsDismountRequested = false;
            }

            var characterPublicState = PlayerCharacter.GetPublicState(character);

            if (characterPublicState.CurrentVehicle != vehicle)
            {
                return;
            }

            InteractableWorldObjectHelper.ServerTryAbortInteraction(character, vehicle);

            vehicle.GetPublicState <VehiclePublicState>()
            .IsDismountRequested = false;

            characterPublicState.ServerSetCurrentVehicle(null);

            var vehiclePublicState = vehicle.GetPublicState <VehiclePublicState>();

            vehiclePublicState.PilotCharacter = null;
            Logger.Important("Player exit vehicle: " + vehicle, character);

            if (!vehicle.IsDestroyed)
            {
                var characterPrivateState = PlayerCharacter.GetPrivateState(character);
                characterPrivateState.LastDismountedVehicleMapMark = new LastDismountedVehicleMapMark(vehicle);
            }

            character.ProtoWorldObject.SharedCreatePhysics(character);

            var protoVehicle = (IProtoVehicle)vehicle.ProtoGameObject;

            if (!vehicle.IsDestroyed)
            {
                vehicle.ProtoWorldObject.SharedCreatePhysics(vehicle);
                // ensure vehicle stopped
                Server.World.SetDynamicObjectPhysicsMovement(vehicle, Vector2D.Zero, targetVelocity: 0);
                Server.World.SetDynamicObjectMoveSpeed(vehicle, 0);
                Server.World.StopPhysicsBody(vehicle.PhysicsBody);
                // no need to do this here, as it's done automatically by vehicle's ServerUpdate method
                //protoVehicle.ServerSetUpdateRate(vehicle, isRare: true);
                protoVehicle.ServerOnCharacterExitVehicle(vehicle, character);
            }

            PlayerCharacter.SharedForceRefreshCurrentItem(character);

            if (vehicle.IsDestroyed)
            {
                return;
            }

            // notify player and other players in scope
            using var tempPlayers = Api.Shared.GetTempList <ICharacter>();
            Server.World.GetScopedByPlayers(vehicle, tempPlayers);
            tempPlayers.Remove(character);

            Instance.CallClient(character,
                                _ => _.ClientRemote_OnVehicleExitByCurrentPlayer(vehicle, protoVehicle));
            Instance.CallClient(tempPlayers.AsList(),
                                _ => _.ClientRemote_OnVehicleExitByOtherPlayer(vehicle,
                                                                               vehicle.Position,
                                                                               protoVehicle));
        }
예제 #3
0
 /// <summary>
 /// Try to deduct energy.
 /// </summary>
 /// <returns>Returns true if there was enough energy and it was deducted.</returns>
 public static bool ServerDeductEnergyCharge(IDynamicWorldObject vehicle, ushort requiredEnergyAmount)
 {
     return(ServerDeductEnergyChargeInternal(vehicle, requiredEnergyAmount));
 }
 public static void ServerUnregisterCurrentMining(IDynamicWorldObject droneObject)
 {
     ServerCurrentMinedObjectsByDrones.RemoveAllByValue(
         existingDroneObject =>
         ReferenceEquals(existingDroneObject, droneObject));
 }
예제 #5
0
 public ViewModelVehicleNameEditorControl(IDynamicWorldObject vehicle)
 {
     this.vehicle = vehicle;
     this.Name    = VehicleNamesSystem.ClientTryGetVehicleName(vehicle.Id)
                    ?? string.Empty;
 }
예제 #6
0
 public static void NotifyObjectDestroyed(ICharacter character, IDynamicWorldObject worldObject)
 {
     Api.SafeInvoke(() => ObjectDestroyed?.Invoke(character, worldObject));
 }
예제 #7
0
 public virtual float SharedGetStructurePointsMax(IDynamicWorldObject worldObject)
 {
     return(this.StructurePointsMax);
 }
예제 #8
0
        public WeaponFinalCache(
            ICharacter character,
            FinalStatsCache characterFinalStatsCache,
            [CanBeNull] IItem weapon,
            [CanBeNull] IProtoItemWeapon protoWeapon,
            [CanBeNull] IProtoItemAmmo protoAmmo,
            DamageDescription damageDescription,
            IProtoExplosive protoExplosive  = null,
            IDynamicWorldObject objectDrone = null)
        {
            this.Character = character;
            this.CharacterFinalStatsCache = characterFinalStatsCache;
            this.Drone          = objectDrone;
            this.Weapon         = weapon;
            this.ProtoWeapon    = (IProtoItemWeapon)weapon?.ProtoItem ?? protoWeapon;
            this.ProtoAmmo      = protoAmmo;
            this.ProtoExplosive = protoExplosive;

            if (damageDescription is null)
            {
                // TODO: it looks like not implemented yet and we should throw an exception here
                // fallback in case weapon don't provide damage description (such as no-ammo weapon)
                damageDescription = new DamageDescription(
                    damageValue: 0,
                    armorPiercingCoef: 0,
                    finalDamageMultiplier: 1,
                    rangeMax: 0,
                    damageDistribution: new DamageDistribution());
            }

            var descriptionDamages        = damageDescription.DamageProportions;
            var damageDistributionsCount  = descriptionDamages.Count;
            var resultDamageDistributions = new List <DamageProportion>(damageDistributionsCount);

            var totalPercents = 0d;

            for (var index = 0; index < damageDistributionsCount; index++)
            {
                var source   = descriptionDamages[index];
                var statName = GetProportionStatName(source.DamageType);
                var resultDamageProportion = source.Proportion + characterFinalStatsCache[statName];
                if (resultDamageProportion <= 0)
                {
                    continue;
                }

                resultDamageDistributions.Add(new DamageProportion(source.DamageType, resultDamageProportion));
                totalPercents += resultDamageProportion;
            }

            if (damageDistributionsCount > 0 &&
                Math.Abs(totalPercents - 1) > 0.001d)
            {
                throw new Exception(
                          "Sum of all damage proportions must be exactly 1. Calculated value: "
                          + totalPercents.ToString("F3"));
            }

            this.DamageDistributions = resultDamageDistributions;

            this.DamageValue = damageDescription.DamageValue * (protoWeapon?.DamageMultiplier ?? 1.0)
                               + characterFinalStatsCache[StatName.DamageAdd];

            var weaponSkillProto = protoWeapon?.WeaponSkillProto;

            if (weaponSkillProto is not null)
            {
                var statName = protoWeapon.WeaponSkillProto.StatNameDamageBonusMultiplier;
                this.DamageValue *= characterFinalStatsCache.GetMultiplier(statName);
            }

            this.RangeMax = damageDescription.RangeMax * (protoWeapon?.RangeMultiplier ?? 1.0)
                            + characterFinalStatsCache[StatName.AttackRangeMax];

            var armorPiercingCoef = (1 + characterFinalStatsCache[StatName.AttackArmorPiercingMultiplier])
                                    * (damageDescription.ArmorPiercingCoef
                                       + characterFinalStatsCache[StatName.AttackArmorPiercingValue]);

            this.InvertedArmorPiercingCoef = 1 - armorPiercingCoef;

            this.FinalDamageMultiplier = damageDescription.FinalDamageMultiplier
                                         + characterFinalStatsCache[StatName.AttackFinalDamageMultiplier];

            var probability = protoWeapon?.SpecialEffectProbability ?? 0;

            if (weaponSkillProto is not null)
            {
                var statNameSpecialEffectChance = weaponSkillProto.StatNameSpecialEffectChanceMultiplier;
                probability *= characterFinalStatsCache.GetMultiplier(statNameSpecialEffectChance);
            }

            this.SpecialEffectProbability = probability;

            this.FireScatterPreset = protoAmmo?.OverrideFireScatterPreset
                                     ?? protoWeapon?.FireScatterPreset
                                     ?? default;
            var shotsPerFire = this.FireScatterPreset.ProjectileAngleOffets.Length;

            if (shotsPerFire > 1)
            {
                // decrease final damage and special effect multiplier on the number of shots per fire
                var coef = 1.0 / shotsPerFire;
                this.FinalDamageMultiplier    *= coef;
                this.SpecialEffectProbability *= coef;
            }
        }
예제 #9
0
 public override IItemsContainer SharedGetHotbarItemsContainer(IDynamicWorldObject vehicle)
 {
     return(GetPrivateState(vehicle).EquipmentItemsContainer);
 }
예제 #10
0
 public void ServerSetupAssociatedItem(IDynamicWorldObject objectDrone, IItem item)
 {
     GetPrivateState(objectDrone).AssociatedItem = item;
 }
예제 #11
0
        private static void ServerOnDroneReturnedToPlayer(IDynamicWorldObject worldObject)
        {
            var privateState = GetPrivateState(worldObject);
            var character    = privateState.CharacterOwner;

            CharacterDroneControlSystem.ServerDespawnDrone(worldObject, isReturnedToPlayer: true);

            var storageContainer = privateState.StorageItemsContainer;

            if (storageContainer.OccupiedSlotsCount == 0)
            {
                return;
            }

            // drop storage container contents to player
            // but first, move the drone item to its original slot (if possible)
            var characterContainerInventory = character.SharedGetPlayerContainerInventory();
            var characterContainerHotbar    = character.SharedGetPlayerContainerHotbar();

            var itemInFirstSlot = storageContainer.GetItemAtSlot(0);

            if (itemInFirstSlot != null)
            {
                // item in the first slot is the drone's associated item
                // it could be destroyed in case the drone's HP dropped <= 1
                Server.Items.MoveOrSwapItem(itemInFirstSlot,
                                            privateState.IsStartedFromHotbarContainer
                                                ? characterContainerHotbar
                                                : characterContainerInventory,
                                            slotId: privateState.StartedFromSlotIndex,
                                            movedCount: out _);
            }

            var result = Server.Items.TryMoveAllItems(storageContainer, characterContainerInventory);

            try
            {
                if (storageContainer.OccupiedSlotsCount == 0)
                {
                    // all items moved from drone to player
                    return;
                }

                // try move remaining items to hotbar
                var resultToHotbar = Server.Items.TryMoveAllItems(storageContainer, characterContainerHotbar);
                result.MergeWith(resultToHotbar,
                                 areAllItemsMoved: resultToHotbar.AreAllItemMoved);

                if (storageContainer.OccupiedSlotsCount == 0)
                {
                    // all items moved from drone to player
                    return;
                }
            }
            finally
            {
                if (result.MovedItems.Count > 0)
                {
                    // notify player about the received items
                    NotificationSystem.ServerSendItemsNotification(
                        character,
                        result.MovedItems
                        .GroupBy(p => p.Key.ProtoItem)
                        .Where(p => !(p.Key is TItemDrone))
                        .ToDictionary(p => p.Key, p => p.Sum(v => v.Value)));
                }
            }

            // try to drop the remaining items on the ground
            var groundContainer = ObjectGroundItemsContainer
                                  .ServerTryGetOrCreateGroundContainerAtTileOrNeighbors(character, character.Tile);

            if (groundContainer != null)
            {
                var protoItemForIcon = storageContainer.Items.First().ProtoItem;
                Server.Items.TryMoveAllItems(storageContainer, groundContainer);
                NotificationSystem.ServerSendNotificationNoSpaceInInventoryItemsDroppedToGround(character,
                                                                                                protoItemForIcon);

                // ensure that these items could be lifted only by their owner in PvE
                WorldObjectClaimSystem.ServerTryClaim(groundContainer.OwnerAsStaticObject,
                                                      character,
                                                      WorldObjectClaimDuration.DroppedGoods);
            }

            if (storageContainer.OccupiedSlotsCount == 0)
            {
                return;
            }

            Logger.Error("Not all items dropped on the ground from the drone storage: "
                         + worldObject
                         + " slots occupied: "
                         + storageContainer.OccupiedSlotsCount);
        }
예제 #12
0
 public override void ServerOnDestroy(IDynamicWorldObject gameObject)
 {
     base.ServerOnDestroy(gameObject);
     CharacterDroneControlSystem.ServerOnDroidDestroyed(gameObject);
 }
예제 #13
0
 public IItemsContainer ServerGetStorageItemsContainer(IDynamicWorldObject objectDrone)
 {
     return(GetPrivateState(objectDrone).StorageItemsContainer);
 }
예제 #14
0
 protected virtual void ClientSetupEngineAudio(
     IDynamicWorldObject vehicle,
     ComponentHoverboardEngineSoundEmitter component)
 {
 }
예제 #15
0
 public static bool SharedHasEnergyCharge(IDynamicWorldObject vehicle, uint energyRequired)
 {
     using var tempItemsList = SharedGetTempListFuelItemsForVehicle(vehicle);
     return(SharedHasEnergyCharge(tempItemsList, energyRequired));
 }
예제 #16
0
 public HUDMechHotbarControl(IDynamicWorldObject vehicle)
 {
     this.vehicle = vehicle;
 }
        public ViewModelWindowObjectVehicle(
            IDynamicWorldObject vehicle,
            FrameworkElement vehicleExtraControl,
            IViewModelWithActiveState vehicleExtraControlViewModel)
        {
            this.VehicleExtraControl          = vehicleExtraControl;
            this.VehicleExtraControlViewModel = vehicleExtraControlViewModel;
            if (vehicleExtraControl is not null)
            {
                vehicleExtraControl.DataContext = vehicleExtraControlViewModel;
            }

            var currentCharacter = Api.Client.Characters.CurrentPlayerCharacter;

            this.ContainerPlayerInventory = (IClientItemsContainer)currentCharacter.SharedGetPlayerContainerInventory();

            this.ProtoVehicle        = (IProtoVehicle)vehicle.ProtoGameObject;
            this.vehiclePublicState  = vehicle.GetPublicState <VehiclePublicState>();
            this.vehiclePrivateState = vehicle.GetPrivateState <VehiclePrivateState>();

            var structurePointsMax = this.ProtoVehicle.SharedGetStructurePointsMax(vehicle);

            this.ViewModelStructurePoints = new ViewModelStructurePointsBarControl()
            {
                ObjectStructurePointsData = new ObjectStructurePointsData(vehicle, structurePointsMax)
            };

            this.ViewModelVehicleEnergy = new ViewModelVehicleEnergy(vehicle);

            this.cargoItemsContainer             = this.vehiclePrivateState.CargoItemsContainer as IClientItemsContainer;
            this.ViewModelItemsContainerExchange = new ViewModelItemsContainerExchange(this.cargoItemsContainer,
                                                                                       callbackTakeAllItemsSuccess:
                                                                                       null)
            {
                IsContainerTitleVisible = false
            };

            this.ViewModelItemsContainerExchange.IsActive = false;

            var isOwner = WorldObjectOwnersSystem.SharedIsOwner(
                ClientCurrentCharacterHelper.Character,
                vehicle);

            this.ViewModelOwnersEditor =
                new ViewModelWorldObjectOwnersEditor(this.vehiclePrivateState.Owners,
                                                     canEditOwners: isOwner ||
                                                     CreativeModeSystem.ClientIsInCreativeMode(),
                                                     callbackServerSetOwnersList:
                                                     ownersList => WorldObjectOwnersSystem.ClientSetOwners(
                                                         vehicle,
                                                         ownersList),
                                                     title: CoreStrings.ObjectOwnersList_Title2);

            this.RefreshCanRepair();

            this.IsVehicleTabActive = true;
            this.ViewModelItemsContainerExchange.IsActive = true;

            if (this.cargoItemsContainer is not null)
            {
                this.cargoItemsContainer.ItemAdded        += this.CargoItemsContainerItemAddedHandler;
                this.cargoItemsContainer.ItemCountChanged += this.CargoItemsContainerItemCountChangedHandler;
            }
        }
 public static void ServerRecallDrone(IDynamicWorldObject objectDrone)
 {
     objectDrone.GetPublicState <DronePublicState>()
     .ResetTargetPosition();
     ServerUnregisterCurrentMining(objectDrone);
 }
예제 #19
0
 protected virtual void ServerOnDynamicObjectDestroyedByCharacter(
     [CanBeNull] ICharacter byCharacter,
     [CanBeNull] IProtoItemWeapon byWeaponProto,
     IDynamicWorldObject targetObject)
 {
 }
예제 #20
0
        private static void SharedShotWeaponHitscan(
            ICharacter character,
            IProtoItemWeapon protoWeapon,
            Vector2D fromPosition,
            WeaponFinalCache weaponCache,
            Vector2D?customTargetPosition,
            IProtoCharacterCore characterProtoCharacter,
            double fireSpreadAngleOffsetDeg,
            CollisionGroup collisionGroup,
            bool isMeleeWeapon,
            IDynamicWorldObject characterCurrentVehicle,
            ProtoSkillWeapons protoWeaponSkill,
            PlayerCharacterSkills playerCharacterSkills,
            ITempList <IWorldObject> allHitObjects)
        {
            Vector2D toPosition;
            var      rangeMax = weaponCache.RangeMax;

            if (customTargetPosition.HasValue)
            {
                var direction = customTargetPosition.Value - fromPosition;
                // ensure the max range is not exceeded
                direction  = direction.ClampMagnitude(rangeMax);
                toPosition = fromPosition + direction;
            }
            else
            {
                toPosition = fromPosition
                             + new Vector2D(rangeMax, 0)
                             .RotateRad(characterProtoCharacter.SharedGetRotationAngleRad(character)
                                        + fireSpreadAngleOffsetDeg * Math.PI / 180.0);
            }

            using var lineTestResults = character.PhysicsBody.PhysicsSpace.TestLine(
                      fromPosition: fromPosition,
                      toPosition: toPosition,
                      collisionGroup: collisionGroup);
            var damageMultiplier    = 1d;
            var hitObjects          = new List <WeaponHitData>(isMeleeWeapon ? 1 : lineTestResults.Count);
            var characterTileHeight = character.Tile.Height;

            if (IsClient ||
                Api.IsEditor)
            {
                SharedEditorPhysicsDebugger.SharedVisualizeTestResults(lineTestResults, collisionGroup);
            }

            var isDamageRayStopped = false;

            foreach (var testResult in lineTestResults.AsList())
            {
                var testResultPhysicsBody = testResult.PhysicsBody;
                var attackedProtoTile     = testResultPhysicsBody.AssociatedProtoTile;
                if (attackedProtoTile != null)
                {
                    if (attackedProtoTile.Kind != TileKind.Solid)
                    {
                        // non-solid obstacle - skip
                        continue;
                    }

                    var attackedTile = IsServer
                                           ? Server.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position)
                                           : Client.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position);

                    if (attackedTile.Height < characterTileHeight)
                    {
                        // attacked tile is below - ignore it
                        continue;
                    }

                    // tile on the way - blocking damage ray
                    isDamageRayStopped = true;
                    var hitData = new WeaponHitData(testResult.PhysicsBody.Position
                                                    + SharedOffsetHitWorldPositionCloserToTileHitboxCenter(
                                                        testResultPhysicsBody,
                                                        testResult.Penetration,
                                                        isRangedWeapon: !isMeleeWeapon));
                    hitObjects.Add(hitData);

                    weaponCache.ProtoWeapon
                    .SharedOnHit(weaponCache,
                                 null,
                                 0,
                                 hitData,
                                 out _);
                    break;
                }

                var damagedObject = testResultPhysicsBody.AssociatedWorldObject;
                if (ReferenceEquals(damagedObject, character) ||
                    ReferenceEquals(damagedObject, characterCurrentVehicle))
                {
                    // ignore collision with self
                    continue;
                }

                if (!(damagedObject.ProtoGameObject is IDamageableProtoWorldObject damageableProto))
                {
                    // shoot through this object
                    continue;
                }

                // don't allow damage is there is no direct line of sight on physical colliders layer between the two objects
                if (SharedHasTileObstacle(character.Position,
                                          characterTileHeight,
                                          damagedObject,
                                          targetPosition: testResult.PhysicsBody.Position
                                          + testResult.PhysicsBody.CenterOffset))
                {
                    continue;
                }

                using (CharacterDamageContext.Create(attackerCharacter: character,
                                                     damagedObject as ICharacter,
                                                     protoWeaponSkill))
                {
                    if (!damageableProto.SharedOnDamage(
                            weaponCache,
                            damagedObject,
                            damageMultiplier,
                            damagePostMultiplier: 1.0,
                            out var obstacleBlockDamageCoef,
                            out var damageApplied))
                    {
                        // not hit
                        continue;
                    }

                    var hitData = new WeaponHitData(damagedObject,
                                                    testResult.Penetration.ToVector2F());
                    weaponCache.ProtoWeapon
                    .SharedOnHit(weaponCache,
                                 damagedObject,
                                 damageApplied,
                                 hitData,
                                 out var isDamageStop);

                    if (isDamageStop)
                    {
                        obstacleBlockDamageCoef = 1;
                    }

                    if (IsServer)
                    {
                        if (damageApplied > 0 &&
                            damagedObject is ICharacter damagedCharacter)
                        {
                            CharacterUnstuckSystem.ServerTryCancelUnstuckRequest(damagedCharacter);
                        }

                        if (damageApplied > 0)
                        {
                            // give experience for damage
                            protoWeaponSkill?.ServerOnDamageApplied(playerCharacterSkills,
                                                                    damagedObject,
                                                                    damageApplied);
                        }
                    }

                    if (obstacleBlockDamageCoef < 0 ||
                        obstacleBlockDamageCoef > 1)
                    {
                        Logger.Error(
                            "Obstacle block damage coefficient should be >= 0 and <= 1 - wrong calculation by "
                            + damageableProto);
                        break;
                    }

                    hitObjects.Add(hitData);

                    if (isMeleeWeapon)
                    {
                        // currently melee weapon could attack only one object on the ray
                        isDamageRayStopped = true;
                        break;
                    }

                    damageMultiplier *= 1.0 - obstacleBlockDamageCoef;
                    if (damageMultiplier <= 0)
                    {
                        // target blocked the damage ray
                        isDamageRayStopped = true;
                        break;
                    }
                }
            }

            var shotEndPosition = GetShotEndPosition(isDamageRayStopped,
                                                     hitObjects,
                                                     toPosition,
                                                     isRangedWeapon: !isMeleeWeapon);

            if (hitObjects.Count == 0)
            {
                protoWeapon.SharedOnMiss(weaponCache,
                                         shotEndPosition);
            }

            SharedCallOnWeaponHitOrTrace(character,
                                         protoWeapon,
                                         weaponCache.ProtoAmmo,
                                         shotEndPosition,
                                         hitObjects,
                                         endsWithHit: isDamageRayStopped);

            foreach (var entry in hitObjects)
            {
                if (!entry.IsCliffsHit &&
                    !allHitObjects.Contains(entry.WorldObject))
                {
                    allHitObjects.Add(entry.WorldObject);
                }
            }
        }