public void StartRecoil(
            ICharacter character,
            IProtoItemWeaponRanged weaponProto,
            IComponentSkeleton skeletonRenderer)
        {
            this.character        = character;
            this.weaponProto      = weaponProto;
            this.skeletonRenderer = skeletonRenderer;
            this.duration         = weaponProto.CharacterAnimationAimingRecoilDuration;
            this.time             = 0;

            // add recoil power
            this.power += this.RandomizeRecoilPower(
                this.weaponProto.CharacterAnimationAimingRecoilPower
                * this.weaponProto.CharacterAnimationAimingRecoilPowerAddCoef);

            if (this.power > this.weaponProto.CharacterAnimationAimingRecoilPower)
            {
                // clamp recoil power
                this.power = this.weaponProto.CharacterAnimationAimingRecoilPower;
            }

            if (!this.IsEnabled)
            {
                this.IsEnabled = true;
            }

            this.Update(0);
        }
コード例 #2
0
 private void ClientRemote_OnExplosion(
     IProtoItemWeaponRanged protoWeapon,
     Vector2D shotSourcePosition,
     Vector2D explosionWorldPosition)
 {
     this.ClientExplodeAt(protoWeapon, shotSourcePosition, explosionWorldPosition);
 }
コード例 #3
0
        private void CreateSpriteRendererAndAnimator(
            out IComponentSpriteRenderer spriteRenderer,
            out ClientComponentSpriteSheetAnimator componentAnimatorFlash,
            byte atlasRow,
            IProtoItemWeaponRanged protoWeapon,
            TextureAtlasResource muzzleFlashTextureAtlas)
        {
            var animationFrameDurationSeconds = this.animationDuration
                                                / (double)muzzleFlashTextureAtlas.AtlasSize.ColumnsCount;

            // create sprite renderer
            spriteRenderer = Api.Client.Rendering.CreateSpriteRenderer(
                this.SceneObject,
                textureResource: TextureResource.NoTexture,
                // draw in the same layer as the skeleton renderer
                // (cannot do Default+1 here as it will produce wrong result over other objects)
                drawOrder: DrawOrder.Default,
                rotationAngleRad: 0,
                // align sprite by left side and centered vertical
                spritePivotPoint: (0, 0.5),
                scale: (float)this.description.TextureScale);

            // to ensure muzzleflash rendering over skeleton renderer we do this offset
            // TODO: find a better way of prioritizing rendering of muzzle flash over skeleton renderer
            spriteRenderer.DrawOrderOffsetY = -0.2;

            // create animator for sprite renderer
            componentAnimatorFlash = this.SceneObject.AddComponent <ClientComponentSpriteSheetAnimator>();
            componentAnimatorFlash.Setup(
                spriteRenderer,
                ClientComponentSpriteSheetAnimator.CreateAnimationFrames(
                    muzzleFlashTextureAtlas,
                    onlySpecificRow: atlasRow),
                animationFrameDurationSeconds);
        }
コード例 #4
0
        private static void SetRecoilAnimation(
            ICharacter character,
            IProtoItemWeaponRanged weaponProto,
            IComponentSkeleton skeletonRenderer)
        {
            var sceneObject     = Api.Client.Scene.GetSceneObject(character);
            var componentRecoil = sceneObject.FindComponent <ClientComponentCharacterWeaponRecoilAnimation>()
                                  ?? sceneObject.AddComponent <ClientComponentCharacterWeaponRecoilAnimation>();

            componentRecoil.StartRecoil(character, weaponProto, skeletonRenderer);
        }
コード例 #5
0
        private Vector2D GetSourcePosition(IProtoItemWeaponRanged protoWeapon)
        {
            // calculate sprite position offset
            var clientState              = this.currentCharacter.GetClientState <BaseCharacterClientState>();
            var skeletonRenderer         = clientState.SkeletonRenderer;
            var slotName                 = clientState.CurrentProtoSkeleton.SlotNameItemInHand;
            var weaponSlotScreenOffset   = skeletonRenderer.GetSlotScreenOffset(attachmentName: slotName);
            var muzzleFlashTextureOffset = protoWeapon.MuzzleFlashDescription.TextureScreenOffset;

            return(skeletonRenderer.TransformSlotPosition(
                       slotName,
                       weaponSlotScreenOffset + (Vector2F)muzzleFlashTextureOffset,
                       out _));
        }
コード例 #6
0
        public void Setup(
            ICharacter character,
            IComponentSkeleton skeletonRenderer,
            IProtoItemWeaponRanged protoWeapon)
        {
            byte flashAtlasRow = 0,
                 smokeAtlasRow = 1;

            this.character        = character;
            this.skeletonRenderer = skeletonRenderer;
            this.protoWeapon      = protoWeapon;
            this.description      = protoWeapon.MuzzleFlashDescription;

            var muzzleFlashTextureAtlas = this.description.TextureAtlas;

            this.animationDuration = this.description.TextureAnimationDurationSeconds;
            this.lightDuration     = this.description.LightDurationSeconds;

            // create light renderer
            this.lightSource = ClientLighting.CreateLightSourceSpot(
                this.SceneObject,
                color: this.description.LightColor,
                spritePivotPoint: (0.5, 0.5),
                size: (float)this.description.LightPower);

            this.CreateSpriteRendererAndAnimator(
                out this.spriteRendererSmoke,
                out var componentAnimatorSmoke,
                smokeAtlasRow,
                protoWeapon,
                muzzleFlashTextureAtlas);

            this.CreateSpriteRendererAndAnimator(
                out this.spriteRendererFlash,
                out var componentAnimatorFlash,
                flashAtlasRow,
                protoWeapon,
                muzzleFlashTextureAtlas);

            this.Destroy(this.animationDuration);
            this.lightSource.Destroy(this.animationDuration);
            this.spriteRendererFlash.Destroy(this.animationDuration);
            this.spriteRendererSmoke.Destroy(this.animationDuration);
            componentAnimatorFlash.Destroy(this.animationDuration);
            componentAnimatorSmoke.Destroy(this.animationDuration);

            this.Update(deltaTime: 0);
        }
コード例 #7
0
        public static void ClientCreateMuzzleFlash(
            IProtoItemWeaponRanged protoWeapon,
            ICharacter characterWorldObject,
            IComponentSkeleton skeletonRenderer)
        {
            if (protoWeapon?.MuzzleFlashDescription.TextureAtlas is null)
            {
                // no muzzle flash defined for this weapon
                return;
            }

            // get scene object (of character) to attach the components to
            var sceneObject = characterWorldObject.ClientSceneObject;

            sceneObject.AddComponent <ClientComponentMuzzleFlash>()
            .Setup(characterWorldObject, skeletonRenderer, protoWeapon);
        }
コード例 #8
0
        private static void CreateMuzzleFlash(
            IProtoItemWeaponRanged protoWeapon,
            ICharacter characterWorldObject,
            IComponentSkeleton skeletonRenderer)
        {
            if (protoWeapon == null)
            {
                // not a ranged weapon
                return;
            }

            // get scene object (of character) to attach the components to
            var sceneObject = Api.Client.Scene.GetSceneObject(characterWorldObject);

            sceneObject.AddComponent <ClientComponentMuzzleFlash>()
            .Setup(characterWorldObject, skeletonRenderer, protoWeapon);
        }
コード例 #9
0
        public void Setup(
            ICharacter character,
            IComponentSkeleton skeletonRenderer,
            IProtoItemWeaponRanged protoWeapon)
        {
            byte flashAtlasRow = 0,
                 smokeAtlasRow = 1;

            this.character        = character;
            this.skeletonRenderer = skeletonRenderer;
            this.protoWeapon      = protoWeapon;
            this.description      = protoWeapon.MuzzleFlashDescription;

            var muzzleFlashTextureAtlas = this.description.TextureAtlas;

            this.animationDuration = this.description.TextureAnimationDurationSeconds;
            this.lightDuration     = this.description.LightDurationSeconds;

            // create light renderer
            this.lightSource = ClientLighting.CreateLightSourceSpot(
                this.SceneObject,
                color: this.description.LightColor,
                spritePivotPoint: (0.5, 0.5),
                size: (float)this.description.LightPower,
                // we don't want to display nickname/healthbar for the firing character, it's too quick anyway
                logicalSize: 0);

            this.CreateSpriteRendererAndAnimator(
                out this.spriteRendererSmoke,
                out this.componentAnimatorSmoke,
                smokeAtlasRow,
                protoWeapon,
                muzzleFlashTextureAtlas);

            this.CreateSpriteRendererAndAnimator(
                out this.spriteRendererFlash,
                out this.componentAnimatorFlash,
                flashAtlasRow,
                protoWeapon,
                muzzleFlashTextureAtlas);

            this.Update(deltaTime: 0);
        }
コード例 #10
0
        private static void CastLine(
            ICharacter character,
            Vector2D?customTargetPosition,
            double rangeMax,
            IProtoItemWeaponRanged protoWeaponRanged,
            CollisionGroup collisionGroup,
            out Vector2D toPosition,
            out Vector2D?hitPosition)
        {
            hitPosition = null;

            var clientState = character.GetClientState <BaseCharacterClientState>();
            var characterRotationAngleRad = clientState.LastInterpolatedRotationAngleRad.HasValue
                                                ? clientState.LastInterpolatedRotationAngleRad.Value
                                                : ((IProtoCharacterCore)character.ProtoCharacter)
                                            .SharedGetRotationAngleRad(character);

            WeaponSystem.SharedCastLine(character,
                                        isMeleeWeapon: false,
                                        rangeMax,
                                        characterRotationAngleRad,
                                        customTargetPosition: customTargetPosition,
                                        fireSpreadAngleOffsetDeg: 0,
                                        collisionGroup: collisionGroup,
                                        toPosition: out toPosition,
                                        tempLineTestResults: out var tempLineTestResults,
                                        sendDebugEvent: false);

            var characterCurrentVehicle = character.SharedGetCurrentVehicle();
            var characterTileHeight     = character.Tile.Height;

            using (tempLineTestResults)
            {
                foreach (var testResult in tempLineTestResults.AsList())
                {
                    var testResultPhysicsBody = testResult.PhysicsBody;

                    var attackedProtoTile = testResultPhysicsBody.AssociatedProtoTile;
                    if (attackedProtoTile is not null)
                    {
                        if (attackedProtoTile.Kind != TileKind.Solid)
                        {
                            // non-solid obstacle - skip
                            continue;
                        }

                        var attackedTile = Client.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position);
                        if (attackedTile.Height < characterTileHeight)
                        {
                            // attacked tile is below - ignore it
                            continue;
                        }

                        // tile on the way - blocking damage ray
                        hitPosition = testResultPhysicsBody.Position
                                      + testResult.Penetration;
                        break;
                    }

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

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

                    if (!protoDamageableWorldObject.SharedIsObstacle(damagedObject, protoWeaponRanged))
                    {
                        // can 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 (WeaponSystem.SharedHasTileObstacle(character.Position,
                                                           characterTileHeight,
                                                           damagedObject,
                                                           targetPosition: testResult.PhysicsBody.Position
                                                           + testResult.PhysicsBody.CenterOffset))
                    {
                        continue;
                    }

                    hitPosition = testResultPhysicsBody.Position
                                  + testResult.Penetration;

                    /*+ WeaponSystem.SharedOffsetHitWorldPositionCloserToObjectCenter(
                     *  damagedObject,
                     *  damagedObject.ProtoWorldObject,
                     *  hitPoint: testResult.Penetration,
                     *  isRangedWeapon: true);*/
                    break;
                }
            }
        }