Example #1
0
        public static void Draw(this GraphicResource res, Canvas c, int frame, float x, float y, Alignment alignment, ColorRgba color, float scaleX = 1f, float scaleY = 1f)
        {
            Texture texture = res.Material.Res.MainTexture.Res;

            if (texture == null)
            {
                return;
            }

            if (frame < 0)
            {
                // ToDo: HUD Animations are slowed down to 0.86f, adjust this in Metadata files
                frame = (int)(Time.GameTimer.TotalSeconds * 0.86f * res.FrameCount / res.FrameDuration) % res.FrameCount;
            }

            Rect  uv = texture.LookupAtlas(frame);
            float w  = texture.InternalWidth * scaleX * uv.W;
            float h  = texture.InternalHeight * scaleY * uv.H;

            Vector2 originPos = new Vector2(x, y);

            alignment.ApplyTo(ref originPos, new Vector2(w, h));

            c.State.SetMaterial(res.Material);
            c.State.ColorTint             = color;
            c.State.TextureCoordinateRect = uv;
            c.FillRect((int)originPos.X, (int)originPos.Y, w, h);
        }
Example #2
0
        protected bool SetTransition(AnimState state, bool cancellable, Action callback = null)
        {
            List <GraphicResource> candidates = FindAnimationCandidates(state);

            if (candidates.Count == 0)
            {
                // ToDo: Cancel previous transition here?
                if (callback != null)
                {
                    callback();
                }

                return(false);
            }
            else
            {
                if (currentTransitionCallback != null)
                {
                    Action oldCallback = currentTransitionCallback;
                    currentTransitionCallback = null;
                    oldCallback();
                }

                currentTransitionCallback = callback;

                currentTransitionState       = state;
                currentTransitionCancellable = cancellable;
                currentTransition            = candidates[0];

                RefreshAnimation();
                return(true);
            }
        }
Example #3
0
        public void CreateSpriteDebris(GraphicResource res, Vector3 pos, int count)
        {
            Material material = res.Material.Res;
            Texture  texture  = material.MainTexture.Res;

            float x = pos.X - res.Base.Hotspot.X;
            float y = pos.Y - res.Base.Hotspot.Y;

            for (int i = 0; i < count; i++)
            {
                float speedX = MathF.Rnd.NextFloat(-1f, 1f) * MathF.Rnd.NextFloat(0.2f, 0.8f) * count;
                debrisList.Add(new DestructibleDebris {
                    Pos          = new Vector3(x, y, pos.Z),
                    Size         = res.Base.FrameDimensions,
                    Speed        = new Vector2(speedX, -1f * MathF.Rnd.NextFloat(2.2f, 4f)),
                    Acceleration = new Vector2(0f, 0.2f),

                    Scale      = 1f,
                    ScaleSpeed = -0.002f,
                    Angle      = MathF.Rnd.NextFloat(MathF.TwoPi),
                    AngleSpeed = speedX * 0.02f,
                    Alpha      = 1f,
                    AlphaSpeed = -0.002f,

                    Time = 560f,

                    Material       = material,
                    MaterialOffset = texture.LookupAtlas(res.FrameOffset + MathF.Rnd.Next(res.FrameCount)),

                    CollisionAction = DebrisCollisionAction.Bounce
                });
            }
        }
Example #4
0
        private void RefreshAnimation()
        {
            GraphicResource resource = (currentTransitionState != AnimState.Idle ? currentTransition : currentAnimation);

            if (renderer == null)
            {
                renderer = AddComponent <ActorRenderer>();
                renderer.AnimationFinished = OnAnimationFinished;
                renderer.AlignToPixelGrid  = true;
                renderer.Offset            = -2000;
                renderer.Flip = (isFacingLeft ? SpriteRenderer.FlipMode.Horizontal : SpriteRenderer.FlipMode.None);
            }

            renderer.SharedMaterial     = resource.Material;
            renderer.FrameConfiguration = resource.Base.FrameConfiguration;

            if (float.IsInfinity(resource.FrameDuration))
            {
                if (resource.FrameCount > 1)
                {
                    renderer.AnimFirstFrame = resource.FrameOffset + MathF.Rnd.Next(resource.FrameCount);
                }
                else
                {
                    renderer.AnimFirstFrame = resource.FrameOffset;
                }

                renderer.AnimLoopMode = ActorRenderer.LoopMode.FixedSingle;
            }
            else
            {
                renderer.AnimFirstFrame = resource.FrameOffset;

                renderer.AnimLoopMode = (resource.OnlyOnce ? ActorRenderer.LoopMode.Once : ActorRenderer.LoopMode.Loop);
            }

            renderer.AnimFrameCount = resource.FrameCount;
            renderer.AnimDuration   = resource.FrameDuration;
            renderer.Rect           = new Rect(
                -resource.Base.Hotspot.X,
                -resource.Base.Hotspot.Y,
                resource.Base.FrameDimensions.X,
                resource.Base.FrameDimensions.Y
                );

            renderer.AnimTime = 0;

            OnAnimationStarted();

            if ((collisionFlags & CollisionFlags.SkipPerPixelCollisions) == 0)
            {
                // ToDo: Workaround for refresh of AABB
                Transform.Pos = Transform.Pos;
            }
        }
Example #5
0
        protected bool SetAnimation(AnimState state)
        {
            if (currentTransitionState != AnimState.Idle && !currentTransitionCancellable)
            {
                return(false);
            }

            if (currentAnimation?.State != null && currentAnimation.State.Contains(state))
            {
                currentAnimationState = state;
                return(false);
            }

            List <GraphicResource> candidates = FindAnimationCandidates(state);

            if (candidates.Count == 0)
            {
                return(false);
            }
            else
            {
                if (currentTransitionState != AnimState.Idle)
                {
                    currentTransitionState = AnimState.Idle;

                    if (currentTransitionCallback != null)
                    {
                        Action oldCallback = currentTransitionCallback;
                        currentTransitionCallback = null;
                        oldCallback();
                    }
                }

                currentAnimationState = state;
                if (candidates.Count > 1)
                {
                    currentAnimation = candidates[MathF.Rnd.Next() % candidates.Count];
                }
                else
                {
                    currentAnimation = candidates[0];
                }

                if (boundingBox.X == 0 || boundingBox.Y == 0)
                {
                    boundingBox = currentAnimation.Base.FrameDimensions - new Point2(2, 2);
                }

                RefreshAnimation();
                return(true);
            }
        }
Example #6
0
        protected void SetAnimation(string identifier)
        {
            currentAnimationState = AnimState.Idle;
            currentAnimation      = availableAnimations[identifier];

            // ToDo: Remove this bounding box reduction
            // ToDo: Move bounding box calculation to Import project
            if (boundingBox.X == 0 || boundingBox.Y == 0)
            {
                boundingBox = currentAnimation.Base.FrameDimensions - new Point2(4, 0);
            }

            RefreshAnimation();
        }
Example #7
0
        // GraphicResources
        public static void Draw(this GraphicResource res, Canvas c, float x, float y, Alignment alignment, ColorRgba color, float scaleX = 1f, float scaleY = 1f)
        {
            Texture texture = res.Material.Res.MainTexture.Res;

            if (texture == null)
            {
                return;
            }

            Vector2 originPos = new Vector2(x, y);

            alignment.ApplyTo(ref originPos, new Vector2(texture.InternalWidth * scaleX, texture.InternalHeight * scaleY));

            c.State.SetMaterial(res.Material);
            c.State.ColorTint = color;
            c.FillRect((int)originPos.X, (int)originPos.Y, texture.InternalWidth * scaleX, texture.InternalHeight * scaleY);
        }
Example #8
0
        public override void OnFixedUpdate(float timeMult)
        {
            if (cooldown > 0f)
            {
                cooldown -= timeMult;
            }
            else
            {
                GraphicResource res      = availableAnimations["AmbientBubbles"];
                Material        material = res.Material.Res;
                Texture         texture  = material.MainTexture.Res;

                for (int i = 0; i < speed; i++)
                {
                    float scale  = MathF.Rnd.NextFloat(0.3f, 1.0f);
                    float speedX = MathF.Rnd.NextFloat(-0.5f, 0.5f) * scale;
                    float speedY = MathF.Rnd.NextFloat(-3f, -2f) * scale;
                    float accel  = MathF.Rnd.NextFloat(-0.008f, -0.001f) * scale;

                    levelHandler.TileMap.CreateDebris(new TileMap.DestructibleDebris {
                        Pos          = Transform.Pos,
                        Size         = res.Base.FrameDimensions,
                        Speed        = new Vector2(speedX, speedY),
                        Acceleration = new Vector2(0f, accel),

                        Scale      = scale,
                        Alpha      = 1f,
                        AlphaSpeed = -0.009f,

                        Time = 110f,

                        Material       = material,
                        MaterialOffset = texture.LookupAtlas(res.FrameOffset + MathF.Rnd.Next(res.FrameCount)),

                        CollisionAction = TileMap.DebrisCollisionAction.None
                    });
                }

                cooldown = 20f;
            }
        }
Example #9
0
        public void CreateParticleDebris(GraphicResource res, Vector3 pos, Vector2 force, int currentFrame, bool isFacingLeft)
        {
            const int debrisSize = 3;

            Material material = res.Material.Res;
            Texture  texture  = material.MainTexture.Res;

            float x = pos.X - res.Base.Hotspot.X;
            float y = pos.Y - res.Base.Hotspot.Y;

            for (int fx = 0; fx < res.Base.FrameDimensions.X; fx += debrisSize + 1)
            {
                for (int fy = 0; fy < res.Base.FrameDimensions.Y; fy += debrisSize + 1)
                {
                    float currentSize = debrisSize * MathF.Rnd.NextFloat(0.2f, 1.1f);
                    debrisList.Add(new DestructibleDebris {
                        Pos   = new Vector3(x + (isFacingLeft ? res.Base.FrameDimensions.X - fx : fx), y + fy, pos.Z),
                        Size  = new Vector2(currentSize /** (isFacingLeft ? -1f : 1f)*/, currentSize),
                        Speed = new Vector2(force.X + ((fx - res.Base.FrameDimensions.X / 2) + MathF.Rnd.NextFloat(-2f, 2f)) * (isFacingLeft ? -1f : 1f) * MathF.Rnd.NextFloat(2f, 8f) / res.Base.FrameDimensions.X,
                                            force.Y - 1f * MathF.Rnd.NextFloat(2.2f, 4f)),
                        Acceleration = new Vector2(0f, 0.2f),

                        Scale      = 1f,
                        Alpha      = 1f,
                        AlphaSpeed = -0.002f,

                        Time = 320f,

                        Material       = material,
                        MaterialOffset = new Rect(
                            (((float)(currentFrame % res.Base.FrameConfiguration.X) / res.Base.FrameConfiguration.X) + ((float)fx / texture.ContentWidth)) * texture.UVRatio.X,
                            (((float)(currentFrame / res.Base.FrameConfiguration.X) / res.Base.FrameConfiguration.Y) + ((float)fy / texture.ContentHeight)) * texture.UVRatio.Y,
                            (currentSize * texture.UVRatio.X / texture.ContentWidth),
                            (currentSize * texture.UVRatio.Y / texture.ContentHeight)
                            ),

                        CollisionAction = DebrisCollisionAction.Bounce
                    });
                }
            }
        }
Example #10
0
        protected bool SetTransition(AnimState state, bool cancellable, Action callback = null)
        {
            List <GraphicResource> candidates = FindAnimationCandidates(state);

            if (candidates.Count == 0)
            {
                if (callback != null)
                {
                    callback();
                }

                return(false);
            }
            else
            {
                if (currentTransitionCallback != null)
                {
                    Action oldCallback = currentTransitionCallback;
                    currentTransitionCallback = null;
                    oldCallback();
                }

                currentTransitionCallback = callback;

                currentTransitionState       = state;
                currentTransitionCancellable = cancellable;
                if (candidates.Count > 1)
                {
                    currentTransition = candidates[MathF.Rnd.Next() % candidates.Count];
                }
                else
                {
                    currentTransition = candidates[0];
                }

                RefreshAnimation();
                return(true);
            }
        }
Example #11
0
        private Metadata RequestMetadataInner(string path, bool async)
        {
            //System.Diagnostics.Debug.WriteLine("Loading metadata \"" + path + "\"...");

#if UNCOMPRESSED_CONTENT
            string pathAbsolute = PathOp.Combine(DualityApp.DataDirectory, "Metadata", path + ".res");
#else
            string pathAbsolute = PathOp.Combine(DualityApp.DataDirectory, "Main.dz", "Metadata", path + ".res");
#endif

            MetadataJson json;
            using (Stream s = FileOp.Open(pathAbsolute, FileAccessMode.Read)) {
                lock (jsonParser) {
                    json = jsonParser.Parse<MetadataJson>(s);
                }
            }

            Metadata metadata = new Metadata();
            metadata.Referenced = true;
            metadata.AsyncFinalizingRequired = async;

            // Pre-load graphics
            if (json.Animations != null) {
                metadata.Graphics = new Dictionary<string, GraphicResource>();

                foreach (KeyValuePair<string, MetadataJson.AnimationsSection> g in json.Animations) {
                    if (g.Value.Path == null) {
                        // No path provided, skip resource...
                        continue;
                    }

#if !THROW_ON_MISSING_RESOURCES
                    try {
#endif
                        bool isIndexed = (g.Value.Flags & 0x02) != 0x00;

                        ColorRgba color;
                        if (g.Value.ShaderColor == null || g.Value.ShaderColor.Count < 4) {
                            color = (isIndexed ? new ColorRgba(0, 255) : ColorRgba.White);
                        } else {
                            color = new ColorRgba((byte)g.Value.ShaderColor[0], (byte)g.Value.ShaderColor[1], (byte)g.Value.ShaderColor[2], (byte)g.Value.ShaderColor[3]);
                        }

                        GenericGraphicResource resBase = RequestGraphicResource(g.Value.Path, async);

                        // Create copy of generic resource
                        GraphicResource res;
                        if (async) {
                            res = GraphicResource.From(resBase, g.Value.Shader, color, isIndexed);
                        } else {
                            ContentRef<DrawTechnique> drawTechnique;
                            if (g.Value.Shader == null) {
                                drawTechnique = (isIndexed ? paletteNormal : basicNormal);
                            } else {
                                drawTechnique = RequestShader(g.Value.Shader);
                            }

                            res = GraphicResource.From(resBase, drawTechnique, color, isIndexed, paletteTexture);
                        }

                        res.FrameOffset = g.Value.FrameOffset;

                        string raw1, raw2; int raw3;
                        if ((raw1 = g.Value.FrameCount as string) != null && int.TryParse(raw1, out raw3)) {
                            res.FrameCount = raw3;
                        } else {
                            res.FrameCount -= res.FrameOffset;
                        }
                        if ((raw2 = g.Value.FrameRate as string) != null && int.TryParse(raw2, out raw3)) {
                            res.FrameDuration = (raw3 <= 0 ? -1 : (1f / raw3) * 5); // ToDo: I don't know...
                        }

                        res.OnlyOnce = (g.Value.Flags & 0x01) != 0x00;

                        if (g.Value.States != null) {
                            res.State = new HashSet<AnimState>();
                            for (int i = 0; i < g.Value.States.Count; i++) {
                                res.State.Add((AnimState)g.Value.States[i]);
                            }
                        }

                        metadata.Graphics[g.Key] = res;
#if !THROW_ON_MISSING_RESOURCES
                    } catch (Exception ex) {
#if !SERVER
                        Log.Write(LogType.Warning, "Can't load animation \"" + g.Key + "\" from metadata \"" + path + "\": " + ex.Message);
#endif
                    }
#endif
                    }
            }

#if !DISABLE_SOUND
            // Pre-load sounds
            if (json.Sounds != null) {
                metadata.Sounds = new Dictionary<string, SoundResource>();

                foreach (var sound in json.Sounds) {
                    if (sound.Value.Paths == null || sound.Value.Paths.Count == 0) {
                        // No path provided, skip resource...
                        continue;
                    }

#if !THROW_ON_MISSING_RESOURCES
                    try {
#endif
                        IList<string> filenames = sound.Value.Paths;
                        ContentRef<AudioData>[] data = new ContentRef<AudioData>[filenames.Count];
                        for (int i = 0; i < data.Length; i++) {
#if UNCOMPRESSED_CONTENT
                            using (Stream s = FileOp.Open(PathOp.Combine(DualityApp.DataDirectory, "Animations", filenames[i]), FileAccessMode.Read))
#else
                            using (Stream s = FileOp.Open(PathOp.Combine(DualityApp.DataDirectory, "Main.dz", "Animations", filenames[i]), FileAccessMode.Read))
#endif
                            {
                                data[i] = new AudioData(s);
                            }
                        }

                        SoundResource resource = new SoundResource();
                        resource.Sound = new Sound(data);
                        metadata.Sounds[sound.Key] = resource;
#if !THROW_ON_MISSING_RESOURCES
                    } catch (Exception ex) {
#if !SERVER
                        Log.Write(LogType.Warning, "Can't load sound \"" + sound.Key + "\" from metadata \"" + path + "\": " + ex.Message);
#endif
                    }
#endif
                    }
                }
#endif

            // Bounding Box
            if (json.BoundingBox != null && json.BoundingBox.Count == 2) {
                metadata.BoundingBox = new Point2(json.BoundingBox[0], json.BoundingBox[1]);
            }

            cachedMetadata[path] = metadata;

            // Request children
            if (json.Preload != null) {
                for (int i = 0; i < json.Preload.Count; i++) {
                    PreloadAsync(json.Preload[i]);
                }
            }

            return metadata;
        }
Example #12
0
        protected virtual void OnFixedUpdate(float timeMult)
        {
            if (currentCarryOver.HasValue)
            {
                bool playersReady = true;
                foreach (Player player in players)
                {
                    // Exit type is already provided
                    playersReady &= player.OnLevelChanging(ExitType.None);
                }

                if (playersReady)
                {
                    if (levelChangeTimer > 0)
                    {
                        levelChangeTimer -= timeMult;
                    }
                    else
                    {
                        root.ChangeLevel(currentCarryOver.Value);
                        currentCarryOver = null;
                        initState        = InitState.Disposed;
                        return;
                    }
                }
            }

            if (difficulty != GameDifficulty.Multiplayer)
            {
                if (players.Count > 0)
                {
                    Vector3 pos = players[0].Transform.Pos;
                    int     tx1 = (int)pos.X >> 5;
                    int     ty1 = (int)pos.Y >> 5;
                    int     tx2 = tx1;
                    int     ty2 = ty1;

#if ENABLE_SPLITSCREEN
                    for (int i = 1; i < players.Count; i++)
                    {
                        Vector3 pos2 = players[i].Transform.Pos;
                        int     tx   = (int)pos2.X >> 5;
                        int     ty   = (int)pos2.Y >> 5;
                        if (tx1 > tx)
                        {
                            tx1 = tx;
                        }
                        else if (tx2 < tx)
                        {
                            tx2 = tx;
                        }
                        if (ty1 > ty)
                        {
                            ty1 = ty;
                        }
                        else if (ty2 < ty)
                        {
                            ty2 = ty;
                        }
                    }
#endif

                    // ToDo: Remove this branching
#if __ANDROID__
                    const int ActivateTileRange = 20;
#else
                    const int ActivateTileRange = 26;
#endif
                    tx1 -= ActivateTileRange;
                    ty1 -= ActivateTileRange;
                    tx2 += ActivateTileRange;
                    ty2 += ActivateTileRange;

                    for (int i = 0; i < actors.Count; i++)
                    {
                        if (actors[i].OnTileDeactivate(tx1 - 2, ty1 - 2, tx2 + 2, ty2 + 2))
                        {
                            i--;
                        }
                    }

                    eventMap.ActivateEvents(tx1, ty1, tx2, ty2, initState != InitState.Initializing);
                }

                eventMap.ProcessGenerators(timeMult);
            }

            ResolveCollisions();

            // Ambient Light Transition
            if (ambientLightCurrent != ambientLightTarget)
            {
                float step = timeMult * 0.012f;
                if (MathF.Abs(ambientLightCurrent - ambientLightTarget) < step)
                {
                    ambientLightCurrent = ambientLightTarget;
                }
                else
                {
                    ambientLightCurrent += step * ((ambientLightTarget < ambientLightCurrent) ? -1 : 1);
                }
            }

            // Weather
            if (weatherType != WeatherType.None && commonResources.Graphics != null)
            {
                // ToDo: Apply weather effect to all other cameras too
                Vector3 viewPos = cameras[0].Transform.Pos;
                for (int i = 0; i < weatherIntensity; i++)
                {
                    TileMap.DebrisCollisionAction collisionAction;
                    if (weatherOutdoors)
                    {
                        collisionAction = TileMap.DebrisCollisionAction.Disappear;
                    }
                    else
                    {
                        collisionAction = (MathF.Rnd.NextFloat() > 0.7f
                            ? TileMap.DebrisCollisionAction.None
                            : TileMap.DebrisCollisionAction.Disappear);
                    }

                    Vector3 debrisPos = viewPos + MathF.Rnd.NextVector3((LevelRenderSetup.TargetSize.X / -2) - 40,
                                                                        (LevelRenderSetup.TargetSize.Y * -2 / 3), MainPlaneZ,
                                                                        LevelRenderSetup.TargetSize.X + 120, LevelRenderSetup.TargetSize.Y, 0);

                    if (weatherType == WeatherType.Rain)
                    {
                        GraphicResource res      = commonResources.Graphics["Rain"];
                        Material        material = res.Material.Res;
                        Texture         texture  = material.MainTexture.Res;

                        float scale  = MathF.Rnd.NextFloat(0.4f, 1.1f);
                        float speedX = MathF.Rnd.NextFloat(2.2f, 2.7f) * scale;
                        float speedY = MathF.Rnd.NextFloat(7.6f, 8.6f) * scale;

                        debrisPos.Z = MainPlaneZ * scale;

                        tileMap.CreateDebris(new TileMap.DestructibleDebris {
                            Pos   = debrisPos,
                            Size  = res.Base.FrameDimensions,
                            Speed = new Vector2(speedX, speedY),

                            Scale = scale,
                            Angle = MathF.Atan2(speedY, speedX),
                            Alpha = 1f,

                            Time = 180f,

                            Material       = material,
                            MaterialOffset = texture.LookupAtlas(res.FrameOffset + MathF.Rnd.Next(res.FrameCount)),

                            CollisionAction = collisionAction
                        });
                    }
                    else
                    {
                        GraphicResource res      = commonResources.Graphics["Snow"];
                        Material        material = res.Material.Res;
                        Texture         texture  = material.MainTexture.Res;

                        float scale  = MathF.Rnd.NextFloat(0.4f, 1.1f);
                        float speedX = MathF.Rnd.NextFloat(-1.6f, -1.2f) * scale;
                        float speedY = MathF.Rnd.NextFloat(3f, 4f) * scale;
                        float accel  = MathF.Rnd.NextFloat(-0.008f, 0.008f) * scale;

                        debrisPos.Z = MainPlaneZ * scale;

                        tileMap.CreateDebris(new TileMap.DestructibleDebris {
                            Pos          = debrisPos,
                            Size         = res.Base.FrameDimensions,
                            Speed        = new Vector2(speedX, speedY),
                            Acceleration = new Vector2(accel, -MathF.Abs(accel)),

                            Scale      = scale,
                            Angle      = MathF.Rnd.NextFloat(MathF.TwoPi),
                            AngleSpeed = speedX * 0.02f,
                            Alpha      = 1f,

                            Time = 180f,

                            Material       = material,
                            MaterialOffset = texture.LookupAtlas(res.FrameOffset + MathF.Rnd.Next(res.FrameCount)),

                            CollisionAction = collisionAction
                        });
                    }
                }
            }

            // Active Boss
            if (activeBoss != null && activeBoss.Scene == null)
            {
                activeBoss = null;

                Hud hud = rootObject.GetComponent <Hud>();
                if (hud != null)
                {
                    hud.ActiveBoss = null;
                }

                InitLevelChange(ExitType.Normal, null);
                levelChangeTimer = 300;
            }

            if (initState == InitState.Initializing)
            {
                initState = InitState.Initialized;
            }


            collisionsCountA = 0;
            collisionsCountB = 0;
            collisionsCountC = 0;
        }
Example #13
0
        private bool IsCollidingWithAngled(ActorBase other)
        {
            const byte AlphaThreshold = 40;

            GraphicResource res1 = (currentTransitionState != AnimState.Idle ? currentTransition : currentAnimation);
            GraphicResource res2 = (other.currentTransitionState != AnimState.Idle ? other.currentTransition : other.currentAnimation);

            PixelData p1 = res1?.Material.Res?.MainTexture.Res?.BasePixmap.Res.PixelData?[0];
            PixelData p2 = res2?.Material.Res?.MainTexture.Res?.BasePixmap.Res.PixelData?[0];

            if (p1 == null || p2 == null)
            {
                return(false);
            }

            Matrix4 transform1 =
                Matrix4.CreateTranslation(new Vector3(-res1.Base.Hotspot.X, -res1.Base.Hotspot.Y, 0f));

            if (isFacingLeft)
            {
                transform1 *= Matrix4.CreateScale(-1f, 1f, 1f);
            }
            transform1 *= Matrix4.CreateRotationZ(Transform.Angle) *
                          Matrix4.CreateTranslation(Transform.Pos);

            Matrix4 transform2 =
                Matrix4.CreateTranslation(new Vector3(-res2.Base.Hotspot.X, -res2.Base.Hotspot.Y, 0f));

            if (other.isFacingLeft)
            {
                transform2 *= Matrix4.CreateScale(-1f, 1f, 1f);
            }
            transform2 *= Matrix4.CreateRotationZ(other.Transform.Angle) *
                          Matrix4.CreateTranslation(other.Transform.Pos);

            int width1  = res1.Base.FrameDimensions.X;
            int height1 = res1.Base.FrameDimensions.Y;
            int width2  = res2.Base.FrameDimensions.X;
            int height2 = res2.Base.FrameDimensions.Y;

            // Bounding Box intersection
            Hitbox box1, box2;

            {
                Vector2 tl = Vector2.Transform(Vector2.Zero, transform1);
                Vector2 tr = Vector2.Transform(new Vector2(width1, 0f), transform1);
                Vector2 bl = Vector2.Transform(new Vector2(0f, height1), transform1);
                Vector2 br = Vector2.Transform(new Vector2(width1, height1), transform1);

                float minX = MathF.Min(tl.X, tr.X, bl.X, br.X);
                float minY = MathF.Min(tl.Y, tr.Y, bl.Y, br.Y);
                float maxX = MathF.Max(tl.X, tr.X, bl.X, br.X);
                float maxY = MathF.Max(tl.Y, tr.Y, bl.Y, br.Y);

                box1 = new Hitbox(
                    MathF.Floor(minX),
                    MathF.Floor(minY),
                    MathF.Ceiling(maxX),
                    MathF.Ceiling(maxY));
            }
            {
                Vector2 tl = Vector2.Transform(Vector2.Zero, transform2);
                Vector2 tr = Vector2.Transform(new Vector2(width2, 0f), transform2);
                Vector2 bl = Vector2.Transform(new Vector2(0f, height2), transform2);
                Vector2 br = Vector2.Transform(new Vector2(width2, height2), transform2);

                float minX = MathF.Min(tl.X, tr.X, bl.X, br.X);
                float minY = MathF.Min(tl.Y, tr.Y, bl.Y, br.Y);
                float maxX = MathF.Max(tl.X, tr.X, bl.X, br.X);
                float maxY = MathF.Max(tl.Y, tr.Y, bl.Y, br.Y);

                box2 = new Hitbox(
                    MathF.Floor(minX),
                    MathF.Floor(minY),
                    MathF.Ceiling(maxX),
                    MathF.Ceiling(maxY));
            }

            if (!box1.Intersects(ref box2))
            {
                return(false);
            }

            // Per-pixel collision check
            Matrix4 transformAToB = transform1 * Matrix4.Invert(transform2);

            // TransformNormal with [1, 0] and [0, 1] vectors
            Vector2 stepX = new Vector2(transformAToB.M11, transformAToB.M12);
            Vector2 stepY = new Vector2(transformAToB.M21, transformAToB.M22);

            Vector2 yPosIn2 = Vector2.Transform(Vector2.Zero, transformAToB);

            int frame1 = MathF.Min(renderer.CurrentFrame, res1.FrameCount - 1);
            int dx1    = (frame1 % res1.Base.FrameConfiguration.X) * res1.Base.FrameDimensions.X;
            int dy1    = (frame1 / res1.Base.FrameConfiguration.X) * res1.Base.FrameDimensions.Y;

            int frame2 = MathF.Min(other.renderer.CurrentFrame, res2.FrameCount - 1);
            int dx2    = (frame2 % res2.Base.FrameConfiguration.X) * res2.Base.FrameDimensions.X;
            int dy2    = (frame2 / res2.Base.FrameConfiguration.X) * res2.Base.FrameDimensions.Y;

            for (int y1 = 0; y1 < height1; y1++)
            {
                Vector2 posIn2 = yPosIn2;

                for (int x1 = 0; x1 < width1; x1++)
                {
                    int x2 = (int)MathF.Round(posIn2.X);
                    int y2 = (int)MathF.Round(posIn2.Y);

                    if (x2 >= 0 && x2 < width2 && y2 >= 0 && y2 < height2)
                    {
                        if (p1[x1 + dx1, y1 + dy1].A > AlphaThreshold && p2[x2 + dx2, y2 + dy2].A > AlphaThreshold)
                        {
                            return(true);
                        }
                    }

                    posIn2 += stepX;
                }

                yPosIn2 += stepY;
            }

            return(false);
        }
Example #14
0
        public bool IsCollidingWith(ActorBase other)
        {
            const byte AlphaThreshold = 40;

            bool perPixel1 = (collisionFlags & CollisionFlags.SkipPerPixelCollisions) == 0;
            bool perPixel2 = (other.collisionFlags & CollisionFlags.SkipPerPixelCollisions) == 0;

            // Limitation - both have to support per-pixel collisions
            if (perPixel1 && perPixel2 && (Transform.Angle != 0f || other.Transform.Angle != 0f))
            {
                return(IsCollidingWithAngled(other));
            }

            GraphicResource res1 = (currentTransitionState != AnimState.Idle ? currentTransition : currentAnimation);
            GraphicResource res2 = (other.currentTransitionState != AnimState.Idle ? other.currentTransition : other.currentAnimation);

            PixelData p1 = res1?.Material.Res?.MainTexture.Res?.BasePixmap.Res.PixelData?[0];
            PixelData p2 = res2?.Material.Res?.MainTexture.Res?.BasePixmap.Res.PixelData?[0];

            if (p1 == null || p2 == null)
            {
                return(false);
            }

            Vector3 pos1 = Transform.Pos;
            Vector3 pos2 = other.Transform.Pos;

            Point2 hotspot1 = res1.Base.Hotspot;
            Point2 hotspot2 = res2.Base.Hotspot;

            Point2 size1 = res1.Base.FrameDimensions;
            Point2 size2 = res2.Base.FrameDimensions;

            Rect box1, box2;

            if (!perPixel1)
            {
                box1 = new Rect(currentHitbox.Left, currentHitbox.Top, currentHitbox.Right - currentHitbox.Left, currentHitbox.Bottom - currentHitbox.Top);
            }
            else if (isFacingLeft)
            {
                box1 = new Rect(pos1.X + hotspot1.X - size1.X, pos1.Y - hotspot1.Y, size1.X, size1.Y);
            }
            else
            {
                box1 = new Rect(pos1.X - hotspot1.X, pos1.Y - hotspot1.Y, size1.X, size1.Y);
            }
            if (!perPixel2)
            {
                box2 = new Rect(other.currentHitbox.Left, other.currentHitbox.Top, other.currentHitbox.Right - other.currentHitbox.Left, other.currentHitbox.Bottom - other.currentHitbox.Top);
            }
            else if (other.isFacingLeft)
            {
                box2 = new Rect(pos2.X + hotspot2.X - size2.X, pos2.Y - hotspot2.Y, size2.X, size2.Y);
            }
            else
            {
                box2 = new Rect(pos2.X - hotspot2.X, pos2.Y - hotspot2.Y, size2.X, size2.Y);
            }

            // Bounding Box intersection
            Rect inter = box1.Intersection(box2);

            if (inter.W <= 0 || inter.H <= 0)
            {
                return(false);
            }

            if (!perPixel1 || !perPixel2)
            {
                if (perPixel1 == perPixel2)
                {
                    return(true);
                }

                PixelData       p;
                GraphicResource res;
                bool            isFacingLeftCurrent;
                int             x1, y1, x2, y2, xs, dx, dy;
                if (perPixel1)
                {
                    p   = p1;
                    res = res1;
                    isFacingLeftCurrent = isFacingLeft;

                    x1 = (int)MathF.Max(inter.X, other.currentHitbox.Left);
                    y1 = (int)MathF.Max(inter.Y, other.currentHitbox.Top);
                    x2 = (int)MathF.Min(inter.RightX, other.currentHitbox.Right);
                    y2 = (int)MathF.Min(inter.BottomY, other.currentHitbox.Bottom);

                    xs = (int)box1.X;

                    int frame1 = Math.Min(renderer.CurrentFrame, res.FrameCount - 1);
                    dx = (frame1 % res.Base.FrameConfiguration.X) * res.Base.FrameDimensions.X;
                    dy = (frame1 / res.Base.FrameConfiguration.X) * res.Base.FrameDimensions.Y - (int)box1.Y;
                }
                else
                {
                    p   = p2;
                    res = res2;
                    isFacingLeftCurrent = other.isFacingLeft;

                    x1 = (int)MathF.Max(inter.X, currentHitbox.Left);
                    y1 = (int)MathF.Max(inter.Y, currentHitbox.Top);
                    x2 = (int)MathF.Min(inter.RightX, currentHitbox.Right);
                    y2 = (int)MathF.Min(inter.BottomY, currentHitbox.Bottom);

                    xs = (int)box2.X;

                    int frame2 = Math.Min(other.renderer.CurrentFrame, res.FrameCount - 1);
                    dx = (frame2 % res.Base.FrameConfiguration.X) * res.Base.FrameDimensions.X;
                    dy = (frame2 / res.Base.FrameConfiguration.X) * res.Base.FrameDimensions.Y - (int)box2.Y;
                }

                // Per-pixel collision check
                for (int i = x1; i < x2; i++)
                {
                    for (int j = y1; j < y2; j++)
                    {
                        int i1 = i - xs;
                        if (isFacingLeftCurrent)
                        {
                            i1 = res.Base.FrameDimensions.X - i1 - 1;
                        }

                        if (p[i1 + dx, j + dy].A > AlphaThreshold)
                        {
                            return(true);
                        }
                    }
                }
            }
            else
            {
                int x1 = (int)inter.X;
                int y1 = (int)inter.Y;
                int x2 = (int)inter.RightX;
                int y2 = (int)inter.BottomY;

                int x1s = (int)box1.X;
                int x2s = (int)box2.X;

                int frame1 = Math.Min(renderer.CurrentFrame, res1.FrameCount - 1);
                int dx1    = (frame1 % res1.Base.FrameConfiguration.X) * res1.Base.FrameDimensions.X;
                int dy1    = (frame1 / res1.Base.FrameConfiguration.X) * res1.Base.FrameDimensions.Y - (int)box1.Y;

                int frame2 = Math.Min(other.renderer.CurrentFrame, res2.FrameCount - 1);
                int dx2    = (frame2 % res2.Base.FrameConfiguration.X) * res2.Base.FrameDimensions.X;
                int dy2    = (frame2 / res2.Base.FrameConfiguration.X) * res2.Base.FrameDimensions.Y - (int)box2.Y;

                // Per-pixel collision check
                for (int i = x1; i < x2; i++)
                {
                    for (int j = y1; j < y2; j++)
                    {
                        int i1 = i - x1s;
                        if (isFacingLeft)
                        {
                            i1 = res1.Base.FrameDimensions.X - i1 - 1;
                        }
                        int i2 = i - x2s;
                        if (other.isFacingLeft)
                        {
                            i2 = res2.Base.FrameDimensions.X - i2 - 1;
                        }

                        if (p1[i1 + dx1, j + dy1].A > AlphaThreshold && p2[i2 + dx2, j + dy2].A > AlphaThreshold)
                        {
                            return(true);
                        }
                    }
                }
            }

            return(false);
        }
Example #15
0
        internal void UpdateAABB()
        {
            if ((collisionFlags & (CollisionFlags.CollideWithOtherActors | CollisionFlags.CollideWithSolidObjects | CollisionFlags.IsSolidObject)) == 0)
            {
                // Collisions are deactivated
                return;
            }

            if ((collisionFlags & CollisionFlags.SkipPerPixelCollisions) == 0)
            {
                GraphicResource res = (currentTransitionState != AnimState.Idle ? currentTransition : currentAnimation);
                if (res == null)
                {
                    return;
                }

                Vector3 pos = Transform.Pos;

                float timeMult = Time.TimeMult;
                pos.X += speedX * timeMult;
                pos.Y += speedY * timeMult;

                Point2 hotspot = res.Base.Hotspot;
                Point2 size    = res.Base.FrameDimensions;

                if (Transform.Angle != 0f)
                {
                    Matrix4 transform1 = Matrix4.CreateTranslation(new Vector3(-hotspot.X, -hotspot.Y, 0f));
                    if (isFacingLeft)
                    {
                        transform1 *= Matrix4.CreateScale(-1f, 1f, 1f);
                    }
                    transform1 *= Matrix4.CreateRotationZ(Transform.Angle) *
                                  Matrix4.CreateTranslation(pos);

                    Vector2 tl = Vector2.Transform(Vector2.Zero, transform1);
                    Vector2 tr = Vector2.Transform(new Vector2(size.X, 0f), transform1);
                    Vector2 bl = Vector2.Transform(new Vector2(0f, size.Y), transform1);
                    Vector2 br = Vector2.Transform(new Vector2(size.X, size.Y), transform1);

                    float minX = MathF.Min(tl.X, tr.X, bl.X, br.X);
                    float minY = MathF.Min(tl.Y, tr.Y, bl.Y, br.Y);
                    float maxX = MathF.Max(tl.X, tr.X, bl.X, br.X);
                    float maxY = MathF.Max(tl.Y, tr.Y, bl.Y, br.Y);

                    AABB.LowerBound = new Vector2(minX, minY);
                    AABB.UpperBound = new Vector2(maxX, maxY);
                }
                else
                {
                    if (isFacingLeft)
                    {
                        AABB.LowerBound = new Vector2(pos.X + hotspot.X - size.X, pos.Y - hotspot.Y);
                    }
                    else
                    {
                        AABB.LowerBound = new Vector2(pos.X - hotspot.X, pos.Y - hotspot.Y);
                    }

                    AABB.UpperBound = AABB.LowerBound + size;
                }
            }
            else
            {
                OnUpdateHitbox();

                AABB = currentHitbox.ToAABB();
            }

#if DEBUG
            Game.UI.Hud.ShowDebugRect(new Rect(AABB.LowerBound.X, AABB.LowerBound.Y, AABB.UpperBound.X - AABB.LowerBound.X, AABB.UpperBound.Y - AABB.LowerBound.Y));
#endif
        }
Example #16
0
        private void FinalizeAsyncLoadedResources(Metadata metadata)
        {
            if (metadata.Graphics != null)
            {
                foreach (var pair in metadata.Graphics)
                {
                    GraphicResource        res     = pair.Value;
                    GenericGraphicResource resBase = res.Base;
                    if (resBase.AsyncFinalize != null)
                    {
                        TextureMagFilter magFilter; TextureMinFilter minFilter;
                        if (resBase.AsyncFinalize.LinearSampling)
                        {
                            magFilter = TextureMagFilter.Linear;
                            minFilter = TextureMinFilter.LinearMipmapLinear;
                        }
                        else
                        {
                            magFilter = TextureMagFilter.Nearest;
                            minFilter = TextureMinFilter.Nearest;
                        }

                        resBase.Texture = new Texture(resBase.AsyncFinalize.TextureMap, TextureSizeMode.NonPowerOfTwo,
                                                      magFilter, minFilter, resBase.AsyncFinalize.TextureWrap, resBase.AsyncFinalize.TextureWrap);

                        if (resBase.AsyncFinalize.TextureNormalMap != null)
                        {
                            resBase.TextureNormal = new Texture(resBase.AsyncFinalize.TextureNormalMap, TextureSizeMode.NonPowerOfTwo,
                                                                magFilter, minFilter, resBase.AsyncFinalize.TextureWrap, resBase.AsyncFinalize.TextureWrap);
                        }

                        resBase.AsyncFinalize = null;
                    }

                    if (res.AsyncFinalize != null)
                    {
                        ContentRef <DrawTechnique> drawTechnique;
                        if (res.AsyncFinalize.Shader == null)
                        {
                            drawTechnique = (res.AsyncFinalize.BindPaletteToMaterial ? paletteNormal : basicNormal);
                        }
                        else
                        {
                            drawTechnique = RequestShader(res.AsyncFinalize.Shader);
                        }

                        Material material = new Material(drawTechnique, res.AsyncFinalize.Color);

                        material.SetTexture("mainTex", resBase.Texture);
                        if (resBase.TextureNormal != null)
                        {
                            material.SetTexture("normalTex", resBase.TextureNormal);
                        }

                        if (res.AsyncFinalize.BindPaletteToMaterial)
                        {
                            material.SetTexture("paletteTex", paletteTexture);
                        }

                        res.Material = material;

                        res.AsyncFinalize = null;
                    }
                }
            }
        }
Example #17
0
        protected void CreateDeathDebris(ActorBase collider)
        {
            TileMap tilemap = levelHandler.TileMap;

            if (tilemap == null)
            {
                return;
            }

            Vector3 pos = Transform.Pos;

            if (collider is AmmoToaster)
            {
                const int debrisSizeX = 5;
                const int debrisSizeY = 3;

                GraphicResource res      = currentTransitionState != AnimState.Idle ? currentTransition : currentAnimation;
                Material        material = res.Material.Res;
                Texture         texture  = material.MainTexture.Res;

                float x = pos.X - res.Base.Hotspot.X;
                float y = pos.Y - res.Base.Hotspot.Y;

                int currentFrame = renderer.CurrentFrame;

                for (int fx = 0; fx < res.Base.FrameDimensions.X; fx += debrisSizeX + 1)
                {
                    for (int fy = 0; fy < res.Base.FrameDimensions.Y; fy += debrisSizeY + 1)
                    {
                        float currentSizeX = debrisSizeX * MathF.Rnd.NextFloat(0.8f, 1.1f);
                        float currentSizeY = debrisSizeY * MathF.Rnd.NextFloat(0.8f, 1.1f);
                        levelHandler.TileMap.CreateDebris(new DestructibleDebris {
                            Pos   = new Vector3(x + (IsFacingLeft ? res.Base.FrameDimensions.X - fx : fx), y + fy, pos.Z),
                            Size  = new Vector2(currentSizeX, currentSizeY),
                            Speed = new Vector2(((fx - res.Base.FrameDimensions.X / 2) + MathF.Rnd.NextFloat(-2f, 2f)) * (IsFacingLeft ? -1f : 1f) * MathF.Rnd.NextFloat(0.5f, 2f) / res.Base.FrameDimensions.X,
                                                MathF.Rnd.NextFloat(0f, 0.2f)),
                            Acceleration = new Vector2(0f, 0.06f),

                            Scale      = 1f,
                            Alpha      = 1f,
                            AlphaSpeed = -0.002f,

                            Time = 320f,

                            Material       = material,
                            MaterialOffset = new Rect(
                                (((float)(currentFrame % res.Base.FrameConfiguration.X) / res.Base.FrameConfiguration.X) + ((float)fx / texture.ContentWidth)) * texture.UVRatio.X,
                                (((float)(currentFrame / res.Base.FrameConfiguration.X) / res.Base.FrameConfiguration.Y) + ((float)fy / texture.ContentHeight)) * texture.UVRatio.Y,
                                (currentSizeX * texture.UVRatio.X / texture.ContentWidth),
                                (currentSizeY * texture.UVRatio.Y / texture.ContentHeight)
                                ),

                            CollisionAction = DebrisCollisionAction.Bounce
                        });
                    }
                }
            }
            else if (pos.Y > levelHandler.WaterLevel)
            {
                const int DebrisSize = 3;

                GraphicResource res      = currentTransitionState != AnimState.Idle ? currentTransition : currentAnimation;
                Material        material = res.Material.Res;
                Texture         texture  = material.MainTexture.Res;

                float x = pos.X - res.Base.Hotspot.X;
                float y = pos.Y - res.Base.Hotspot.Y;

                for (int fx = 0; fx < res.Base.FrameDimensions.X; fx += DebrisSize + 1)
                {
                    for (int fy = 0; fy < res.Base.FrameDimensions.Y; fy += DebrisSize + 1)
                    {
                        float currentSize = DebrisSize * MathF.Rnd.NextFloat(0.2f, 1.1f);
                        levelHandler.TileMap.CreateDebris(new DestructibleDebris {
                            Pos   = new Vector3(x + (IsFacingLeft ? res.Base.FrameDimensions.X - fx : fx), y + fy, pos.Z),
                            Size  = new Vector2(currentSize /** (isFacingLeft ? -1f : 1f)*/, currentSize),
                            Speed = new Vector2(((fx - res.Base.FrameDimensions.X / 2) + MathF.Rnd.NextFloat(-2f, 2f)) * (IsFacingLeft ? -1f : 1f) * MathF.Rnd.NextFloat(1f, 3f) / res.Base.FrameDimensions.X,
                                                ((fy - res.Base.FrameDimensions.Y / 2) + MathF.Rnd.NextFloat(-2f, 2f)) * (IsFacingLeft ? -1f : 1f) * MathF.Rnd.NextFloat(1f, 3f) / res.Base.FrameDimensions.Y),
                            Acceleration = new Vector2(0f, 0f),

                            Scale      = 1f,
                            Alpha      = 1f,
                            AlphaSpeed = -0.004f,

                            Time = 340f,

                            Material       = material,
                            MaterialOffset = new Rect(
                                (((float)(renderer.CurrentFrame % res.Base.FrameConfiguration.X) / res.Base.FrameConfiguration.X) + ((float)fx / texture.ContentWidth)) * texture.UVRatio.X,
                                (((float)(renderer.CurrentFrame / res.Base.FrameConfiguration.X) / res.Base.FrameConfiguration.Y) + ((float)fy / texture.ContentHeight)) * texture.UVRatio.Y,
                                (currentSize * texture.UVRatio.X / texture.ContentWidth),
                                (currentSize * texture.UVRatio.Y / texture.ContentHeight)
                                ),

                            CollisionAction = DebrisCollisionAction.Disappear
                        });
                    }
                }
            }
            else
            {
                Vector2 force;
                switch (lastHitDir)
                {
                case LastHitDirection.Left: force = new Vector2(-1.4f, 0f); break;

                case LastHitDirection.Right: force = new Vector2(1.4f, 0f); break;

                case LastHitDirection.Up: force = new Vector2(0f, -1.4f); break;

                case LastHitDirection.Down: force = new Vector2(0f, 1.4f); break;

                default: force = Vector2.Zero; break;
                }

                tilemap.CreateParticleDebris(currentTransitionState != AnimState.Idle ? currentTransition : currentAnimation,
                                             Transform.Pos, force, renderer.CurrentFrame, IsFacingLeft);
            }
        }
Example #18
0
        private void OnUpdate()
        {
            if (currentCarryOver.HasValue)
            {
                if (levelChangeTimer > 0)
                {
                    levelChangeTimer -= Time.TimeMult;
                }
                else
                {
                    root.ChangeLevel(currentCarryOver.Value);
                    currentCarryOver = null;
                    return;
                }
            }

            Vector3 pos = players[0].Transform.Pos;
            //int tx = (int)(pos.X / 32);
            //int ty = (int)(pos.Y / 32);
            int tx = (int)pos.X >> 5;
            int ty = (int)pos.Y >> 5;

            // ToDo: Remove this branching
#if __ANDROID__
            const int ActivateTileRange = 20;
#else
            const int ActivateTileRange = 26;
#endif

            for (int i = 0; i < actors.Count; i++)
            {
                if (actors[i].OnTileDeactivate(tx, ty, ActivateTileRange + 2))
                {
                    i--;
                }
            }

            eventMap.ActivateEvents(tx, ty, ActivateTileRange);

            eventMap.ProcessGenerators();

            ResolveCollisions();

            // Ambient Light Transition
            if (ambientLightCurrent != ambientLightTarget)
            {
                float step = Time.TimeMult * 0.012f;
                if (MathF.Abs(ambientLightCurrent - ambientLightTarget) < step)
                {
                    ambientLightCurrent = ambientLightTarget;
                }
                else
                {
                    ambientLightCurrent += step * ((ambientLightTarget < ambientLightCurrent) ? -1 : 1);
                }
            }

            // Weather
            if (weatherType != WeatherType.None)
            {
                Vector3 viewPos = camera.Transform.Pos;
                for (int i = 0; i < weatherIntensity; i++)
                {
                    TileMap.DebrisCollisionAction collisionAction;
                    if (weatherOutdoors)
                    {
                        collisionAction = TileMap.DebrisCollisionAction.Disappear;
                    }
                    else
                    {
                        collisionAction = (MathF.Rnd.NextFloat() > 0.7f
                            ? TileMap.DebrisCollisionAction.None
                            : TileMap.DebrisCollisionAction.Disappear);
                    }

                    Vector3 debrisPos = viewPos + MathF.Rnd.NextVector3((LevelRenderSetup.TargetSize.X / -2) - 40,
                                                                        (LevelRenderSetup.TargetSize.Y * -2 / 3), MainPlaneZ,
                                                                        LevelRenderSetup.TargetSize.X + 120, LevelRenderSetup.TargetSize.Y, 0);

                    if (weatherType == WeatherType.Rain)
                    {
                        GraphicResource res      = commonResources.Graphics["Rain"];
                        Material        material = res.Material.Res;
                        Texture         texture  = material.MainTexture.Res;

                        float scale  = MathF.Rnd.NextFloat(0.4f, 1.1f);
                        float speedX = MathF.Rnd.NextFloat(2.2f, 2.7f) * scale;
                        float speedY = MathF.Rnd.NextFloat(7.6f, 8.6f) * scale;

                        debrisPos.Z = MainPlaneZ * scale;

                        tileMap.CreateDebris(new TileMap.DestructibleDebris {
                            Pos   = debrisPos,
                            Size  = res.Base.FrameDimensions,
                            Speed = new Vector2(speedX, speedY),

                            Scale = scale,
                            Angle = MathF.Atan2(speedY, speedX),
                            Alpha = 1f,

                            Time = 180f,

                            Material       = material,
                            MaterialOffset = texture.LookupAtlas(res.FrameOffset + MathF.Rnd.Next(res.FrameCount)),

                            CollisionAction = collisionAction
                        });
                    }
                    else
                    {
                        GraphicResource res      = commonResources.Graphics["Snow"];
                        Material        material = res.Material.Res;
                        Texture         texture  = material.MainTexture.Res;

                        float scale  = MathF.Rnd.NextFloat(0.4f, 1.1f);
                        float speedX = MathF.Rnd.NextFloat(-1.6f, -1.2f) * scale;
                        float speedY = MathF.Rnd.NextFloat(3f, 4f) * scale;
                        float accel  = MathF.Rnd.NextFloat(-0.008f, 0.008f) * scale;

                        debrisPos.Z = MainPlaneZ * scale;

                        tileMap.CreateDebris(new TileMap.DestructibleDebris {
                            Pos          = debrisPos,
                            Size         = res.Base.FrameDimensions,
                            Speed        = new Vector2(speedX, speedY),
                            Acceleration = new Vector2(accel, -MathF.Abs(accel)),

                            Scale      = scale,
                            Angle      = MathF.Rnd.NextFloat(MathF.TwoPi),
                            AngleSpeed = speedX * 0.02f,
                            Alpha      = 1f,

                            Time = 180f,

                            Material       = material,
                            MaterialOffset = texture.LookupAtlas(res.FrameOffset + MathF.Rnd.Next(res.FrameCount)),

                            CollisionAction = collisionAction
                        });
                    }
                }
            }

            // Active Boss
            if (activeBoss != null && activeBoss.ParentScene == null)
            {
                activeBoss = null;

                Hud hud = rootObject.GetComponent <Hud>();
                if (hud != null)
                {
                    hud.ActiveBoss = null;
                }

                InitLevelChange(ExitType.Normal, null);
                levelChangeTimer *= 2;
            }

            if (DualityApp.Keyboard.KeyHit(Key.Escape))
            {
                Scene.SwitchTo(new InGameMenu(root, this));
            }

            Hud.ShowDebugText("- FPS: " + Time.Fps.ToString("N0") + "  (" + Math.Round(Time.UnscaledDeltaTime * 1000, 1).ToString("N1") + " ms)");
            Hud.ShowDebugText("  Diff.: " + difficulty + " | Actors: " + actors.Count.ToString("N0"));
            Hud.ShowDebugText("  Ambient Light: " + ambientLightCurrent.ToString("0.00") + " / " + ambientLightTarget.ToString("0.00"));


            Hud.ShowDebugText("  Collisions: " + collisionsCountA + " > " + collisionsCountB + " > " + collisionsCountC);
            collisionsCountA = 0;
            collisionsCountB = 0;
            collisionsCountC = 0;
        }