Esempio n. 1
0
        public HelirinState Next(HelirinState st, Action a)
        {
            if (st == null)
            {
                return(null);
            }
            if (st.IsTerminal())
            {
                return(st);
            }

            ActionEffect e = Controller.action_to_effect(a);

            st = st.ShallowCopy();

            st.frameNumber += 1;

            if (st.life > Settings.full_life)
            {
                st.life = Settings.full_life;
            }
            if (st.invul > Settings.invul_frames)
            {
                st.invul = Settings.invul_frames;
            }

            // 1. Set input speed (XS and YS) depending on inputs
            int[] speeds = input_speeds;
            if (e.x != Direction1.None && e.y != Direction1.None)
            {
                speeds = input_speeds_2;
            }
            int speed = speeds[(int)e.speed];
            int xs    = (int)e.x * speed;
            int ys    = (int)e.y * speed;

            // 2. Reduce bump speed / bump rotation (XB, YB and Rot_rate) / invulnerability
            short rot_diff = (short)(st.rot_srate - st.rot_rate);

            if (rot_diff < -rotation_rate_decr)
            {
                rot_diff = -rotation_rate_decr;
            }
            if (rot_diff > rotation_rate_decr)
            {
                rot_diff = rotation_rate_decr;
            }
            st.rot_rate = (short)(st.rot_rate + rot_diff);
            st.xb       = st.xb * bump_speed_decrease_numerator / bump_speed_decrease_denominator;
            st.yb       = st.yb * bump_speed_decrease_numerator / bump_speed_decrease_denominator;
            if (st.invul > 0)
            {
                st.invul--;
            }

            // 3. Move depending on speed (bump+input), rotate depending on rotation rate (Rot_rate)
            st.xpos += xs + st.xb;
            st.ypos += ys + st.yb;
            st.rot  += st.rot_rate;

            // 4. Detection of healing/ending zones
            // Position seems to be converted to px with a shift: subpixels seem to be ignored even in negative positions.
            bool safe_zone = false;

            Map.Zone zone = map.IsPixelInZone(pos_to_px(st.xpos), pos_to_px(st.ypos));
            if (zone == Map.Zone.Healing || zone == Map.Zone.Starting)
            {
                safe_zone = true;
                if (st.life < Settings.full_life)
                {
                    st.life = Settings.full_life;
                }
            }
            if (zone == Map.Zone.Ending)
            {
                st.gs = st.gs == GameState.InGameWithBonus ? GameState.WinWithBonus : GameState.Win;
                return(st);
            }

            // At this point, we backup the rotation data (will be needed later)
            short rot_rate_bkp = st.rot_rate; // No need to backup st.rot because it will not change anymore

            // We also precompute all the helirin physical points and the collision mask
            // TODO: Optionally, use memoisation to avoid recomputing collision mask each time
            short[] pxs            = new short[helirin_points.Length];
            short[] pys            = new short[helirin_points.Length];
            uint    collision_mask = 0;

            for (int i = 0; i < helirin_points.Length; i++)
            {
                int radius = helirin_points[i];
                // Position seems to be converted to px BEFORE adding the result of the sin/cos (it seems to ignore subpixels, even in negative positions).
                short px = (short)(pos_to_px(st.xpos) - math.sin(radius, st.rot));
                short py = (short)(pos_to_px(st.ypos) + math.cos(radius, st.rot));
                pxs[i] = px;
                pys[i] = py;
                // Compute collision mask
                if (map.IsPixelInCollision(px, py))
                {
                    collision_mask |= ((uint)1 << i);
                }
            }

            // 5. Action of springs & bonus
            HashSet <int> spring_already_visited = new HashSet <int>();
            bool          invert_rotation        = false;
            bool          update_rot_rate        = false;

            foreach (int i in helirin_points_order_for_springs) // Order is important for spring actions.
            {
                int   radius = helirin_points[i];
                short px     = pxs[i];
                short py     = pys[i];

                // Action of springs
                Map.Spring[] springs = map.IsPixelInSpring(px, py);
                foreach (Map.Spring spr in springs)
                {
                    if (!spring_already_visited.Contains(spr.unique_id))
                    {
                        spring_already_visited.Add(spr.unique_id);

                        if (radius != 0)
                        {
                            update_rot_rate = true;
                            // Invert rotation if at least one spring is in the right direction
                            if (!invert_rotation)
                            {
                                short spring_angle         = AngleOfSpring(spr.type);
                                short helirin_angle        = radius > 0 ? st.rot : (short)(st.rot + (0x10000 / 2));
                                short helirin_normal_angle = (short)(helirin_angle + Math.Sign(st.rot_srate) * (0x10000 / 4));
                                short diff = (short)(spring_angle - helirin_normal_angle);
                                if (Math.Abs((int)diff) > 0x10000 / 4)
                                {
                                    invert_rotation = true;
                                }
                            }
                        }

                        // Position bump
                        if (spr.type == Map.SpringType.Up)
                        {
                            st.xb = 0;
                            st.yb = -bump_speed_spring;
                        }
                        if (spr.type == Map.SpringType.Down)
                        {
                            st.xb = 0;
                            st.yb = bump_speed_spring;
                        }
                        if (spr.type == Map.SpringType.Left)
                        {
                            st.xb = -bump_speed_spring;
                            st.yb = 0;
                        }
                        if (spr.type == Map.SpringType.Right)
                        {
                            st.xb = bump_speed_spring;
                            st.yb = 0;
                        }
                        st.xpos += st.xb;
                        st.ypos += st.yb;
                    }
                }
                // Action of bonus
                if (map.IsPixelInBonus(px, py) != Map.BonusType.None)
                {
                    st.gs = GameState.InGameWithBonus;
                }
            }
            if (invert_rotation)
            {
                st.rot_srate = (short)(-st.rot_srate);
            }
            if (update_rot_rate)
            {
                st.rot_rate = (short)(Math.Sign(st.rot_srate) * rot_bump_rate_spring);
                if (st.rot_rate == 0)
                {
                    st.rot_rate = -rot_bump_rate_spring;
                }
            }

            // 6. Action of moving objects
            uint object_collision_mask = 0;

            if (Settings.enable_moving_objects)
            {
                List <Roller.Ball> balls = new List <Roller.Ball>();
                foreach (Roller r in map.Rollers)
                {
                    Roller.Ball ball = r.PreciseBoxAtTime(st.frameNumber);
                    if (ball != null)
                    {
                        balls.Add(ball);
                    }
                }
                balls.Sort((x, y) => x < y ? -1 : (x > y ? 1 : 0));
                foreach (Roller.Ball ball in balls)
                {
                    uint elt_collision_mask = 0;
                    for (int i = 0; i < helirin_points.Length; i++)
                    {
                        // For rollers, position of physical points must be recomputed to take into account last position/rotation changes
                        // EDIT: Seems not... At least, spring actions should not affect it

                        /*int radius = helirin_points[i];
                         * short px = (short)(pos_to_px(st.xpos) - math.sin(radius, st.rot));
                         * short py = (short)(pos_to_px(st.ypos) + math.cos(radius, st.rot));*/
                        short px = pxs[i];
                        short py = pys[i];
                        if (ball.InCollisionWith(px, py))
                        {
                            elt_collision_mask |= ((uint)1 << i);
                        }
                    }
                    if (elt_collision_mask != 0)
                    {
                        object_collision_mask |= elt_collision_mask;
                        ObjectHitReact(st, elt_collision_mask, /*pxs[0], pys[0],*/ pos_to_px(st.xpos), pos_to_px(st.ypos), ball.cx, ball.cy);
                    }
                }
                foreach (Piston p in map.Pistons)
                {
                    Rectangle?box = null;
                    uint      elt_collision_mask = 0;
                    for (int i = 0; i < helirin_points.Length; i++)
                    {
                        short px = pxs[i];
                        short py = pys[i];
                        if (p.dangerArea.Contains(px, py))
                        {
                            if (box == null)
                            {
                                box = p.PreciseBoxAtTime(st.frameNumber);
                            }
                            if (box.Value.Contains(px, py))
                            {
                                elt_collision_mask |= ((uint)1 << i);
                            }
                        }
                    }
                    if (elt_collision_mask != 0)
                    {
                        object_collision_mask |= elt_collision_mask;
                        ObjectHitReact(st, elt_collision_mask, /*pxs[0], pys[0],*/ pos_to_px(st.xpos), pos_to_px(st.ypos),
                                       box.Value.X + (box.Value.Width - 1) / 2, box.Value.Y + (box.Value.Height - 1) / 2);
                    }
                }
            }

            if (collision_mask != 0 || object_collision_mask != 0) // If collision with a wall OR a moving object
            {
                // 7. Damage and substract input speed (XS and YS) to position
                if (!safe_zone)
                {
                    if (st.invul == 0)
                    {
                        st.invul = Settings.invul_frames;
                        st.life--;
                    }
                }
                st.xpos -= xs;
                st.ypos -= ys;

                if (collision_mask != 0) // If collision with a wall
                {
                    // 8. Bump action
                    // - Modify bump speed and rot rate (XB, YB and Rot_rate) accordingly if relevant
                    // - If modified, apply this newly computed bump speed to position
                    bool up_side   = (collision_mask & up_mask) != 0;
                    bool down_side = (collision_mask & down_mask) != 0;
                    if (up_side && !down_side)
                    {
                        st.xb = -math.sin(auto_bump_speed, st.rot);
                        st.yb = math.cos(auto_bump_speed, st.rot);
                    }
                    if (!up_side && down_side)
                    {
                        st.xb = math.sin(auto_bump_speed, st.rot);
                        st.yb = -math.cos(auto_bump_speed, st.rot);
                    }
                    st.rot_rate = (short)(-Math.Sign(rot_rate_bkp) * rot_bump_rate);
                    if (st.rot_rate == 0)
                    {
                        st.rot_rate = rot_bump_rate;
                    }
                    if (up_side != down_side)
                    {
                        st.xpos += st.xb;
                        st.ypos += st.yb;
                    }

                    // 9. If mask has collision at one of the 3 lowest bits :
                    //  - Modify bump speed (XB and YB) depending on input (if any)
                    //  - If modified, apply this newly computed bump speed to position
                    if ((collision_mask & middle_mask) != 0 && (e.x != Direction1.None || e.y != Direction1.None))
                    {
                        int bump_speed = input_bump_speed;
                        if (e.x != Direction1.None && e.y != Direction1.None)
                        {
                            bump_speed = input_bump_speed_2;
                        }
                        st.xb    = -(int)e.x * bump_speed;
                        st.yb    = -(int)e.y * bump_speed;
                        st.xpos += st.xb;
                        st.ypos += st.yb;
                    }
                }
            }

            // Lose?
            // TODO: Move up so that some useless computation can be avoided when losing
            if (st.life == 0)
            {
                st.gs = GameState.Lose;
            }

            return(st);
        }
Esempio n. 2
0
        protected void Redraw()
        {
            if (m != null)
            {
                Bitmap bitmap;
                int    width   = showP ? 3 * m.WidthPx : m.WidthPx;
                int    height  = showP ? 3 * m.HeightPx : m.HeightPx;
                int    start_x = showP ? -m.WidthPx : 0;
                int    start_y = showP ? -m.HeightPx : 0;

                if (start_pixel.HasValue)
                {
                    start_x = start_pixel.Value.x;
                    start_y = start_pixel.Value.y;
                }
                if (start_pixel.HasValue && end_pixel.HasValue)
                {
                    width  = end_pixel.Value.x - start_pixel.Value.x + 1;
                    height = end_pixel.Value.y - start_pixel.Value.y + 1;
                }
                if (showC && cost_map != null)
                {
                    width  = Math.Min(width, cost_map.Width);
                    height = Math.Min(height, cost_map.Height);
                }
                bitmap = new Bitmap(width, height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                computed_start_pixel = new Pixel((short)start_x, (short)start_y);

                Graphics g = Graphics.FromImage(bitmap);
                g.Clear(BackColor);

                Brush startingBrush    = new SolidBrush(Color.FromArgb(0x77, 0xD0, 0x00, 0x00));
                Brush healingBrush     = new SolidBrush(Color.FromArgb(0x77, 0x40, 0x40, 0xFF));
                Brush endingBrush      = new SolidBrush(Color.FromArgb(0x77, 0xD0, 0xD0, 0x00));
                Brush springBrush      = new SolidBrush(Color.FromArgb(0x77, 0x80, 0x80, 0x80));
                Brush collisionBrush   = Brushes.SlateGray;
                Brush phyStartingBrush = new SolidBrush(Color.FromArgb(0x55, 0xD0, 0x00, 0x00));
                Brush phyHealingBrush  = new SolidBrush(Color.FromArgb(0x55, 0x40, 0x40, 0xFF));
                Brush phyEndingBrush   = new SolidBrush(Color.FromArgb(0x55, 0xD0, 0xD0, 0x00));
                Brush phySpringBrush   = new SolidBrush(Color.FromArgb(0x55, 0x80, 0x80, 0x80));

                if (showC && cost_map != null)
                {
                    float max = cost_map.GetMaxWeightExceptInfinity(cost_map_real_invul);
                    if (max <= 0)
                    {
                        max = float.Epsilon;
                    }
                    for (int y = 0; y < bitmap.Height; y++)
                    {
                        for (int x = 0; x < bitmap.Width; x++)
                        {
                            Rectangle dest  = new Rectangle(x, y, 1, 1);
                            float     cost  = cost_map.CostAtIndex(x, y, cost_map_real_invul);
                            int       color = cost < float.PositiveInfinity ? (int)((cost / max) * 255) : 255;
                            color = 255 - color;
                            Brush brush = new SolidBrush(Color.FromArgb(255, color, color, color));
                            g.FillRectangle(brush, dest);
                        }
                    }
                }
                else
                {
                    if (showP)
                    {
                        for (int y = 0; y < bitmap.Height; y++)
                        {
                            for (int x = 0; x < bitmap.Width; x++)
                            {
                                short xp = (short)(x + start_x);
                                short yp = (short)(y + start_y);

                                Rectangle dest = new Rectangle(x, y, 1, 1);
                                if (m.IsPixelInCollision(xp, yp))
                                {
                                    g.FillRectangle(collisionBrush, dest);
                                }

                                Map.Zone zone = m.IsPixelInZone(xp, yp);
                                if (zone == Map.Zone.Starting)
                                {
                                    g.FillRectangle(phyStartingBrush, dest);
                                }
                                else if (zone == Map.Zone.Healing)
                                {
                                    g.FillRectangle(phyHealingBrush, dest);
                                }
                                else if (zone == Map.Zone.Ending)
                                {
                                    g.FillRectangle(phyEndingBrush, dest);
                                }

                                Map.Spring[] springs = m.IsPixelInSpring(xp, yp);
                                foreach (Map.Spring s in springs)
                                {
                                    g.FillRectangle(phySpringBrush, dest);
                                }
                            }
                        }
                    }

                    if (showG)
                    {
                        for (int y = 0; y < bitmap.Height; y += Map.tile_size)
                        {
                            for (int x = 0; x < bitmap.Width; x += Map.tile_size)
                            {
                                int tile_x = x + start_x;
                                int tile_y = y + start_y;
                                if (tile_x < 0 || tile_y < 0 || tile_x >= m.WidthPx || tile_y >= m.HeightPx)
                                {
                                    continue;
                                }
                                ushort tile = m.TileAt(tile_x / Map.tile_size, tile_y / Map.tile_size);

                                int       x_offset = tile_x % Map.tile_size;
                                int       y_offset = tile_y % Map.tile_size;
                                Rectangle dest     = new Rectangle(x - x_offset, y - y_offset, Map.tile_size, Map.tile_size);
                                Rectangle?sprite   = m.GetTileSprite(tile);
                                if (sprite.HasValue)
                                {
                                    g.DrawImage(Resources.sprites, dest, sprite.Value, GraphicsUnit.Pixel);
                                }

                                Map.Zone zone = m.IsTileAZone(tile);
                                if (zone == Map.Zone.Starting)
                                {
                                    g.FillRectangle(startingBrush, dest);
                                }
                                else if (zone == Map.Zone.Healing)
                                {
                                    g.FillRectangle(healingBrush, dest);
                                }
                                else if (zone == Map.Zone.Ending)
                                {
                                    g.FillRectangle(endingBrush, dest);
                                }

                                if (m.IsTileASpring(tile).HasValue)
                                {
                                    dest = new Rectangle(dest.X, dest.Y, Map.spring_size, Map.spring_size);
                                    g.FillRectangle(springBrush, dest);
                                }
                            }
                        }
                    }
                }

                if (showO)
                {
                    void drawRectangle(Rectangle r, Brush brush)
                    {
                        g.FillRectangle(brush, r.X - start_x, r.Y - start_y, r.Width, r.Height);
                    }

                    Brush b = new SolidBrush(Color.FromArgb(0x44, 0xAA, 0x22, 0x77));
                    foreach (Piston p in m.Pistons)
                    {
                        drawRectangle(p.dangerArea, b);
                    }
                    b = new SolidBrush(Color.FromArgb(0x44, 0xAA, 0x77, 0x22));
                    foreach (Roller r in m.Rollers)
                    {
                        drawRectangle(r.dangerArea, b);
                    }
                }

                bmap = bitmap;
            }
            else
            {
                bmap = null;
            }

            Refresh();
        }
Esempio n. 3
0
 public bool IsHealZone(short x, short y)
 {
     Map.Zone zone = m.IsPixelInZone(x, y);
     return(zone == Map.Zone.Healing || zone == Map.Zone.Starting);
 }