Exemple #1
0
        private static void SharedFireWeapon(
            ICharacter character,
            IItem weaponItem,
            IProtoItemWeapon protoWeapon,
            WeaponState weaponState)
        {
            protoWeapon.SharedOnFire(character, weaponState);

            var playerCharacterSkills = character.SharedGetSkills();
            var protoWeaponSkill      = playerCharacterSkills != null
                                       ? protoWeapon.WeaponSkillProto
                                       : null;

            if (IsServer)
            {
                // give experience for shot
                protoWeaponSkill?.ServerOnShot(playerCharacterSkills);
            }

            var weaponCache = weaponState.WeaponCache;

            if (weaponCache == null)
            {
                // calculate new weapon cache
                RebuildWeaponCache(character, weaponState);
                weaponCache = weaponState.WeaponCache;
            }

            // raycast possible victims
            var fromPosition = character.Position
                               + (0, character.ProtoCharacter.CharacterWorldWeaponOffset);

            var toPosition = fromPosition
                             + new Vector2D(weaponCache.RangeMax, 0)
                             .RotateRad(character.ProtoCharacter.SharedGetRotationAngleRad(character));

            var collisionGroup = protoWeapon is IProtoItemWeaponMelee
                                     ? CollisionGroups.HitboxMelee
                                     : CollisionGroups.HitboxRanged;

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

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

                        // tile on the way - blocking damage ray
                        break;
                    }

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

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

                    if (!damageableProto.SharedOnDamage(
                            weaponCache,
                            damagedObject,
                            damageMultiplier,
                            out var obstacleBlockDamageCoef,
                            out var damageApplied))
                    {
                        // not hit
                        continue;
                    }

                    if (IsServer)
                    {
                        weaponCache.ProtoWeapon
                        .ServerOnDamageApplied(weaponCache.Weapon, character, damagedObject, damageApplied);

                        if (damageApplied > 0 &&
                            protoWeaponSkill != null)
                        {
                            // give experience for damage
                            protoWeaponSkill.ServerOnDamageApplied(playerCharacterSkills, damagedObject, damageApplied);

                            if (damagedObject is ICharacter damagedCharacter &&
                                damagedCharacter.GetPublicState <ICharacterPublicState>().CurrentStats.HealthCurrent
                                <= 0)
                            {
                                // give weapon experience for kill
                                Logger.Info("Killed " + damagedCharacter, character);
                                protoWeaponSkill.ServerOnKill(playerCharacterSkills, killedCharacter: damagedCharacter);

                                if (damagedCharacter.ProtoCharacter is ProtoCharacterMob protoMob)
                                {
                                    // give hunting skill experience for mob kill
                                    var experience = SkillHunting.ExperienceForKill;
                                    experience *= protoMob.MobKillExperienceMultiplier;
                                    if (experience > 0)
                                    {
                                        playerCharacterSkills.ServerAddSkillExperience <SkillHunting>(experience);
                                    }
                                }
                            }
                        }
                    }

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

                    //var hitPosition = testResultPhysicsBody.Position + testResult.Penetration;
                    hitObjects.Add(new WeaponHitData(damagedObject)); //, hitPosition));

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

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

                if (hitObjects.Count > 0)
                {
                    if (IsClient)
                    {
                        // display weapon shot on Client-side
                        WeaponSystemClientDisplay.OnWeaponHit(protoWeapon, hitObjects);
                    }
                    else // if server
                    {
                        // display damages on clients in scope of every damaged object
                        using (var scopedBy = Api.Shared.GetTempList <ICharacter>())
                        {
                            foreach (var hitObject in hitObjects)
                            {
                                if (hitObject.WorldObject.IsDestroyed)
                                {
                                    continue;
                                }

                                Server.World.GetScopedByPlayers(hitObject.WorldObject, scopedBy);
                                scopedBy.Remove(character);
                                if (scopedBy.Count == 0)
                                {
                                    continue;
                                }

                                Instance.CallClient(scopedBy,
                                                    _ => _.ClientRemote_OnWeaponHit(protoWeapon, hitObject));
                                scopedBy.Clear();
                            }
                        }
                    }
                }

                if (IsServer)
                {
                    protoWeapon.ServerOnShot(character, weaponItem, protoWeapon, hitObjects);
                }
            }
        }
        private static void SharedFireWeapon(
            ICharacter character,
            IItem weaponItem,
            IProtoItemWeapon protoWeapon,
            WeaponState weaponState)
        {
            if (!protoWeapon.SharedOnFire(character, weaponState))
            {
                return;
            }

            var playerCharacterSkills = character.SharedGetSkills();
            var protoWeaponSkill      = playerCharacterSkills != null
                                       ? protoWeapon.WeaponSkillProto
                                       : null;

            if (IsServer)
            {
                protoWeaponSkill?.ServerOnShot(playerCharacterSkills); // give experience for shot
                CharacterUnstuckSystem.ServerTryCancelUnstuckRequest(character);
            }

            var weaponCache = weaponState.WeaponCache;

            if (weaponCache is null)
            {
                SharedRebuildWeaponCache(character, weaponState);
                weaponCache = weaponState.WeaponCache;
            }

            var characterCurrentVehicle = character.IsNpc
                                              ? null
                                              : character.SharedGetCurrentVehicle();

            var isMeleeWeapon            = protoWeapon is IProtoItemWeaponMelee;
            var characterProtoCharacter  = (IProtoCharacterCore)character.ProtoCharacter;
            var fromPosition             = characterProtoCharacter.SharedGetWeaponFireWorldPosition(character, isMeleeWeapon);
            var fireSpreadAngleOffsetDeg = protoWeapon.SharedUpdateAndGetFirePatternCurrentSpreadAngleDeg(weaponState);

            var collisionGroup = protoWeapon.CollisionGroup;

            using var allHitObjects = Shared.GetTempList <IWorldObject>();
            var shotsPerFire = weaponCache.FireScatterPreset.ProjectileAngleOffets;

            foreach (var angleOffsetDeg in shotsPerFire)
            {
                SharedShotWeaponHitscan(character,
                                        protoWeapon,
                                        fromPosition,
                                        weaponCache,
                                        weaponState.CustomTargetPosition,
                                        characterProtoCharacter,
                                        fireSpreadAngleOffsetDeg + angleOffsetDeg,
                                        collisionGroup,
                                        isMeleeWeapon,
                                        characterCurrentVehicle,
                                        protoWeaponSkill,
                                        playerCharacterSkills,
                                        allHitObjects);
            }

            if (IsServer)
            {
                protoWeapon.ServerOnShot(character, weaponItem, protoWeapon, allHitObjects.AsList());
            }
        }