import java.applet.Applet;
import java.awt.*;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.geom.Point2D;
import engine.Karu;
import engine.KaruProg;
import engine.krBitmap;
import engine.krSound;


public class BubblefishBob extends Applet
{
    private static final long serialVersionUID = 1L;

    
    public class ProgBob implements KaruProg, FocusListener
    {
        class Bubble
        {
            double x;
            double y;
            double t;
            double phase;
            krBitmap bm;
            Bubble next;
            Bubble prev;
            boolean shot;
            boolean fish_inside;
            double attach_x;
            double attach_y;
            double trans;
            byte combo;
        }

        
        class PathPoint
        {
            double x;
            double y;
            double dist_to_next;
        }

        
        class Item
        {
            int type;
            double x;
            double y;
            double py;
            double vel_y;
            double time_existed;
            krBitmap bm;
            Bonus bonus;
            Item next;
        }

        
        class PartString extends Part
        {
            public PartString(double f, double f1, krBitmap krbitmap, double f2)
            {
                super(f, f1, krbitmap, f2);
            }

            public void Draw()
            {
                if(life < 0.25)
                    karu.gr.setFont(fnts[(int)(4 * life * 10)]);
                else
                    karu.gr.setFont(fnts[9]);
                karu.gr.setColor(Color.white);
                karu.DrawTextCentered((int)x, (int)y, str);
            }

            public String str;
        }

        
        class PartBub extends Part
        {
            public PartBub(double f, double f1, krBitmap krbitmap, double f2)
            {
                super(f, f1, krbitmap, f2);
            }

            public void Draw()
            {
                bm = bm_part_bub[(int)((1 - life) * 4)];
                karu.DrawBitmap(bm, (int)x, (int)y);
            }
        }

 
        class Part
        {
            public Part(double f, double f1, krBitmap krbitmap, double f2)
            {
                x = f;
                y = f1;
                bm = krbitmap;
                life = 0.99;
                life_speed = f2;
            }

            public void Draw()
            {
                karu.DrawBitmap(bm, (int)x, (int)y);
            }

            double x;
            double y;
            double vx;
            double vy;
            krBitmap bm;
            double life;
            double life_speed;
            Part next;
        }

        
        class BonusSmRocks extends Bonus
        {
            public BonusSmRocks(String s, krBitmap krbitmap)
            {
                super.name = new String(s);
                super.icon = krbitmap;
            }

            public void Act(Bubble bubble)
            {
                smrocks = 6;
            }
        }

        
        class BonusTorpedo extends Bonus
        {
            public BonusTorpedo(String s, krBitmap krbitmap)
            {
                super.name = new String(s);
                super.icon = krbitmap;
            }

            public void Act(Bubble bubble)
            {
                torpedo = true;
            }
        }

        
        class BonusRewind extends Bonus
        {
            public BonusRewind(String s, krBitmap krbitmap)
            {
                super.name = new String(s);
                super.icon = krbitmap;
            }

            public void Act(Bubble bubble)
            {
                if(time_rewind > 0)
                    time_rewind = 3;
                else
                    time_rewind = 4;
            }
        }


        class BonusPause extends Bonus
        {
            public BonusPause(String s, krBitmap krbitmap)
            {
                super.name = new String(s);
                super.icon = krbitmap;
            }

            public void Act(Bubble bubble)
            {
                if(time_paused > 0)
                    time_paused = 5;
                else
                    time_paused = 6;
            }
        }


        abstract class Bonus
        {
            public abstract void Act(Bubble bubble);

            String name;
            krBitmap icon;
            Bonus next;
        }



        public ProgBob()
        {
            bonuses = null;
            items = null;
            path_speed = 0.4;
            bob_y = 290;
            shoot_time = 0;
            tp = new Point2D.Double();
            path_inc_x = 0;
            path_inc_y = 0;
            tim = 0;
        }

        boolean IsSomeBonusActing()
        {
            return time_rewind > 0 || time_paused > 0 || items != null || smrocks > 0 || torpedo;
        }

        public void AddPart(Part part)
        {
            if(parts == null)
            {
                parts = part;
            } else
            {
                part.next = parts;
                parts = part;
            }
        }

        public void SpawnBiggerBurst(double f, double f1)
        {
            for(int i = 0; i < 30; i++)
            {
                double f2 = (double)(Math.random() * 2 * Math.PI);
                double f3 = (double)(140 + Math.random() * 170);
                PartBub partbub = new PartBub(f, f1, bm_part_bub[0], 3 + (double)(0.8 * Math.random()));
                partbub.life += (double)(0.4 * Math.random());
                partbub.vx = (double)((double)f3 * Math.cos(f2));
                partbub.vy = (double)((double)f3 * Math.sin(f2));
                partbub.x += ((Part) (partbub)).vx / 80;
                partbub.y += ((Part) (partbub)).vy / 80;
                AddPart(partbub);
            }

        }

        public void SpawnBurst(double f, double f1)
        {
            f1 += 5;
            for(int i = 0; i < 26; i++)
            {
                double f2 = (double)(Math.random() * 2 * Math.PI);
                double f3 = (double)(50 + Math.random() * 50);
                PartBub partbub = new PartBub(f, f1, bm_part_bub[0], 3 + (double)(1.2 * Math.random()));
                partbub.life += (double)(0.2 * Math.random());
                partbub.vx = (double)((double)f3 * Math.cos(f2));
                partbub.vy = (double)((double)f3 * Math.sin(f2)) - f3;
                partbub.x += ((Part) (partbub)).vx / 80;
                partbub.y += ((Part) (partbub)).vy / 80;
                AddPart(partbub);
            }

        }

        public void DrawParts()
        {
            for(Part part = parts; part != null; part = part.next)
                if(part.life < 0.999)
                    part.Draw();
        }

        public void UpdateParts(double f)
        {
            Part part = null;
            for(Part part1 = parts; part1 != null; part1 = part1.next)
            {
                part1.life -= part1.life_speed * f;
                if(part1.life <= 0.001)
                {
                    if(part == null)
                        parts = part1.next;
                    else
                        part.next = part1.next;
                } else
                if(part1.life < 0.999)
                {
                    part1.x += f * part1.vx;
                    part1.y += f * part1.vy;
                    part = part1;
                }
            }
        }

        public void DrawItems()
        {
            for(Item item = items; item != null; item = item.next)
            {
                int i = (int)(item.x + 3 * Math.sin(2 * Math.PI * akey1));
                int j = (int)item.y;
                if(4 * item.time_existed < 1 && item.vel_y > 0)
                {
                    double f = 4 * item.time_existed;
                    int k = (int)((double)item.bm.img.getWidth(null) * f);
                    int l = (int)((double)item.bm.img.getHeight(null) * f);
                    karu.gr.drawImage(item.bm.img, i - k / 2, j - l / 2, k, l, null);
                } else
                {
                    karu.DrawBitmap(item.bm, i - item.bm.img.getWidth(null) / 2, j - item.bm.img.getHeight(null) / 2);
                }
            }

            if(shot_bubble.bm != null)
                karu.DrawBitmap(shot_bubble.bm, (int)(shot_bubble.x - 13), (int)(shot_bubble.y - 13));
        }

        public void UpdateItems(double f)
        {
            Item item = null;
            for(Item item1 = items; item1 != null; item1 = item1.next)
            {
                item1.y += f * item1.vel_y;
                if(item1.y < -21 || item1.y > 380 || item1.type == 0)
                {
                    if(item == null)
                        items = items.next;
                    else
                        item.next = item1.next;
                } else
                {
                    item1.time_existed += f;
                    if(item1.type == 2 || item1.type == 4)
                    {
                        if(item1.type == 2)
                            item1.vel_y -= 400 * f;
                        for(; Math.abs(item1.y - item1.py) > 4; item1.py -= 4)
                        {
                            PartBub partbub = new PartBub(item1.x, item1.py, bm_part_bub[0], 1 + (double)(1.2 * Math.random()));
                            partbub.vx = 20 * (double)(Math.random() - 0.5);
                            partbub.vy = -10 - 40 * (double)Math.random();
                            partbub.x += 10 * (double)(Math.random() - 0.5);
                            AddPart(partbub);
                        }

                        for(Bubble bubble = first_bub; bubble != null; bubble = bubble.next)
                        {
                            double f4 = item1.x - bubble.x;
                            double f7 = item1.y - bubble.y;
                            if(f4 * f4 + f7 * f7 < 169)
                            {
                                switch(item1.type)
                                {
                                default:
                                    break;

                                case 2: // '\002'
                                    for(Bubble bubble1 = first_bub; bubble1 != null; bubble1 = bubble1.next)
                                    {
                                        SpawnBiggerBurst(item1.x, item1.y);
                                        double f5 = item1.x - bubble1.x;
                                        double f8 = item1.y - bubble1.y;
                                        if(f5 * f5 + f8 * f8 < 4096 && path_t0 + bubble1.t > 0)
                                        {
                                            SpawnBurst(bubble1.x, bubble1.y);
                                            if(bubble1.fish_inside)
                                            {
                                                fish_to_save--;
                                                total_fish_saved++;
                                                Part part = new Part(bubble1.x, bubble1.y, bm_smfish, 0.4);
                                                part.vx = (double)(-140 + Math.random() * -40);
                                                part.vy = (double)((Math.random() - 0.5) * 40);
                                                AddPart(part);
                                            }
                                            if(bubble1.next != null)
                                            {
                                                bubble1.next.shot = true;
                                                bubble1.next.prev = bubble1.prev;
                                            }
                                            if(bubble1.prev != null)
                                                bubble1.prev.next = bubble1.next;
                                            else
                                                first_bub = bubble1.next;
                                        }
                                    }

                                    karu.PlaySound(snd_explosion);
                                    shoot_time = 0.5;
                                    break;

                                case 4: // '\004'
                                    SpawnBurst(bubble.x, bubble.y);
                                    if(bubble.fish_inside)
                                    {
                                        fish_to_save--;
                                        total_fish_saved++;
                                        Part part1 = new Part(bubble.x, bubble.y, bm_smfish, 0.4);
                                        part1.vx = (double)(-140 + Math.random() * -40);
                                        part1.vy = (double)((Math.random() - 0.5) * 40);
                                        AddPart(part1);
                                    }
                                    if(bubble.next != null)
                                    {
                                        bubble.next.shot = true;
                                        bubble.next.prev = bubble.prev;
                                    }
                                    if(bubble.prev != null)
                                        bubble.prev.next = bubble.next;
                                    else
                                        first_bub = bubble.next;
                                    karu.PlaySound(snd_pop);
                                    break;
                                }
                                item1.type = 0;
                                if(first_bub == null)
                                    if(fish_to_save <= 0)
                                    {
                                        level_completed = true;
                                    } else
                                    {
                                        first_bub = new Bubble();
                                        first_bub.t = -path_t0 - 1;
                                        totally_random_bubble = true;
                                        first_bub.bm = GetRandomBubble();
                                        totally_random_bubble = false;
                                        first_bub.fish_inside = true;
                                    }
                            }
                        }

                    } else
                    {
                        for(; Math.abs(item1.y - item1.py) > 4; item1.py += 4)
                        {
                            PartBub partbub1 = new PartBub(item1.x, item1.py, bm_part_bub[0], 1 + (double)(1.2 * Math.random()));
                            partbub1.vx = 10 * (double)(Math.random() - 0.5);
                            partbub1.vy = -10 - 40 * (double)Math.random();
                            partbub1.x += 13 * (double)(Math.random() - 0.5);
                            AddPart(partbub1);
                        }

                        if(item1.bonus != null && Math.abs(item1.x - (double)bob_x) < 25 && Math.abs(item1.y - (double)(bob_y + 20)) < 38)
                        {
                            SpawnBurst(item1.x, item1.y);
                            karu.PlaySound(snd_pick);
                            PartString partstring = new PartString(item1.x, item1.y, null, 0.6);
                            partstring.vx = 0;
                            partstring.vy = -30;
                            partstring.str = item1.bonus.name;
                            AddPart(partstring);
                            shoot_time = 0.5;
                            item1.bonus.Act(null);
                            if(item == null)
                                items = items.next;
                            else
                                item.next = item1.next;
                        }
                    }
                    item = item1;
                }
            }

        }

        public void EventKeyPress(int i)
        {
            if(i == 32)
                if(game_state == 1)
                {
                    paused = !paused;
                    karu.PlaySound(snd_pick);
                } else
                {
                    paused = false;
                }
        }

        public void EventMouseClick(int i, int j, int k)
        {
            if(game_state == 2)
            {
                karu.PlaySound(snd_level_start);
                game_state = 1;
                name_show = 0;
                game_over = false;
                st_timer = 0;
                level_completed = false;
                int l = ((level - 1) + 1) / 5 + 1;
                if(l >= 4)
                {
                    completed_the_game = true;
                    game_state = 3;
                }
                InitLevel(level + 1);
                return;
            }
            if(game_state == 3)
            {
                int i1 = GetMenuSel(i, j);
                if(i1 == 0)
                {
                    karu.PlaySound(snd_level_start);
                    game_state = 1;
                    name_show = 0;
                    game_over = false;
                    st_timer = 0;
                    InitGame();
                    InitLevel(1);
                }
                return;
            }
            if(game_state == 0)
            {
                karu.PlaySound(snd_level_start);
                game_state = 1;
                game_over = false;
                st_timer = 0;
                InitGame();
                InitLevel(1);
                return;
            }
            if(paused)
                return;
            if(smrocks > 0)
            {
                smrocks--;
                karu.PlaySound(snd_shoot_bubble);
                Item item = new Item();
                item.x = i;
                item.y = item.py = bob_y + 0;
                item.vel_y = -360;
                item.type = 4;
                item.bonus = null;
                item.bm = bm_smrock;
                if(items == null)
                {
                    items = item;
                } else
                {
                    item.next = items.next;
                    items.next = item;
                }
                shoot_time = 0.5;
                PartString partstring = new PartString(item.x, item.y, null, 0.6);
                partstring.str = Integer.toString(smrocks);
                partstring.vx = 0;
                partstring.vy = -30;
                AddPart(partstring);
                return;
            }
            if(torpedo)
            {
                torpedo = false;
                karu.PlaySound(snd_shoot_bubble);
                Item item1 = new Item();
                item1.x = i;
                item1.y = item1.py = bob_y + 0;
                item1.vel_y = -120;
                item1.type = 2;
                item1.bonus = null;
                item1.bm = bm_torpedo;
                if(items == null)
                {
                    items = item1;
                } else
                {
                    item1.next = items.next;
                    items.next = item1;
                }
                shoot_time = 0.5;
                return;
            }
            if(shot_bubble.bm != null)
                return;
            if(shoot_time < 0.8)
                return;
            if(k == 2)
            {
                krBitmap krbitmap = next_bubble;
                next_bubble = next_bubble2;
                next_bubble2 = krbitmap;
                karu.PlaySound(snd_swap);
                shoot_time = 0.5;
            } else
            {
                shoot_time = 0;
                shot_bubble.bm = next_bubble;
                next_bubble = next_bubble2;
                next_bubble2 = GetRandomBubble();
                shot_bubble.x = i;
                shot_bubble.y = bob_y - 10;
                karu.PlaySound(snd_shoot_bubble);
            }
        }

        public void GetPathPoint(Point2D.Double pnt, double f)
        {
            f *= 26;
            double f1 = 0;
            if(f < 0)
            {
                pnt.x = -30;
                pnt.y = -400;
                return;
            }
            for(int i = 0; i < num_path_points; i++)
            {
                double f2 = f1 + path[i].dist_to_next;
                if(f < f2)
                {
                    double f3 = (f - f1) / path[i].dist_to_next;
                    pnt.x = path[i].x + (path[i + 1].x - path[i].x) * f3;
                    pnt.y = path[i].y + (path[i + 1].y - path[i].y) * f3;
                    return;
                }
                f1 = f2;
            }

            pnt.x = path[num_path_points - 1].x;
            pnt.y = path[num_path_points - 1].y;
        }

        public void SetPathPoint(int i, double f, double f1)
        {
            f += path_inc_x;
            f1 += path_inc_y - 10;
            f *= 0.71;
            f1 *= 0.71;
            path[i] = new PathPoint();
            path[i].x = f;
            path[i].y = f1;
            if(i > 0)
                path[i - 1].dist_to_next = (double)Math.sqrt((path[i - 1].x - f) * (path[i - 1].x - f) + (path[i - 1].y - f1) * (path[i - 1].y - f1));
            num_path_points = i + 1;
        }

        public void InitGame()
        {
            completed_the_game = false;
            handicap = 1;
            total_fish_saved = 0;
            total_score = level_score = 0;
            game_over = level_completed = false;
            shoot_time = 0;
            paused = false;
        }

        public void InitLevel(int i)
        {
            paused = false;
            handicap = 1;
            longest_combo = 0;
            torpedo = false;
            smrocks = 0;
            name_show = 0;
            time_rewind = time_paused = 0;
            items = null;
            game_starting = true;
            totally_random_bubble = true;
            level_score = 0;
            go_speedup = 0;
            st_timer = 0;
            level = i;
            shot_bubble = new Bubble();
            shot_bubble.bm = null;
            int j = 0;
            path_speed = 0.5;
            byte byte0 = 5;
            episode = (level - 1) / byte0;
            sub_level = (level - 1) % byte0 + 1;
            if(sub_level == 1)
            {
                fish_to_save_at_start = fish_to_save = 80;
                path_speed = 0.4;
                path_inc_x = 320;
                path_inc_y = -10;
                path_start_t = 32;
                num_path_points = 53;
                path = new PathPoint[num_path_points];
                SetPathPoint(j++, 0, 0);
                SetPathPoint(j++, 52, 6);
                SetPathPoint(j++, 6, 72);
                SetPathPoint(j++, -36, 91);
                SetPathPoint(j++, -86, 90);
                SetPathPoint(j++, -129, 90);
                SetPathPoint(j++, -171, 89);
                SetPathPoint(j++, -205, 89);
                SetPathPoint(j++, -225, 89);
                SetPathPoint(j++, -252, 101);
                SetPathPoint(j++, -281, 123);
                SetPathPoint(j++, -275, 146);
                SetPathPoint(j++, -267, 172);
                SetPathPoint(j++, -249, 187);
                SetPathPoint(j++, -218, 190);
                SetPathPoint(j++, -184, 188);
                SetPathPoint(j++, -159, 175);
                SetPathPoint(j++, -131, 162);
                SetPathPoint(j++, -89, 161);
                SetPathPoint(j++, -52, 170);
                SetPathPoint(j++, -28, 181);
                SetPathPoint(j++, -1, 181);
                SetPathPoint(j++, 33, 168);
                SetPathPoint(j++, 63, 149);
                SetPathPoint(j++, 103, 136);
                SetPathPoint(j++, 141, 132);
                SetPathPoint(j++, 180, 132);
                SetPathPoint(j++, 196, 154);
                SetPathPoint(j++, 233, 180);
                SetPathPoint(j++, 245, 206);
                SetPathPoint(j++, 235, 238);
                SetPathPoint(j++, 196, 259);
                SetPathPoint(j++, 139, 265);
                SetPathPoint(j++, 79, 270);
                SetPathPoint(j++, 39, 272);
                SetPathPoint(j++, 3, 272);
                SetPathPoint(j++, -21, 267);
                SetPathPoint(j++, -55, 267);
                SetPathPoint(j++, -97, 267);
                SetPathPoint(j++, -128, 267);
                SetPathPoint(j++, -176, 269);
                SetPathPoint(j++, -213, 271);
                SetPathPoint(j++, -251, 291);
                SetPathPoint(j++, -257, 326);
                SetPathPoint(j++, -235, 351);
                SetPathPoint(j++, -187, 367);
                SetPathPoint(j++, -133, 367);
                SetPathPoint(j++, -75, 368);
                SetPathPoint(j++, 2, 366);
                SetPathPoint(j++, 58, 363);
                SetPathPoint(j++, 112, 350);
                SetPathPoint(j++, 174, 353);
                SetPathPoint(j++, 190, 356);
            } else
            if(sub_level == 2)
            {
                fish_to_save_at_start = fish_to_save = 180;
                path_speed = 0.5;
                path_inc_x = 0;
                path_inc_y = 59;
                path_start_t = 14;
                num_path_points = 78;
                path = new PathPoint[num_path_points];
                SetPathPoint(j++, -30, 0);
                SetPathPoint(j++, 20, 0);
                SetPathPoint(j++, 57, 0);
                SetPathPoint(j++, 105, 1);
                SetPathPoint(j++, 145, 0);
                SetPathPoint(j++, 213, -2);
                SetPathPoint(j++, 272, 5);
                SetPathPoint(j++, 295, 27);
                SetPathPoint(j++, 291, 57);
                SetPathPoint(j++, 268, 75);
                SetPathPoint(j++, 235, 82);
                SetPathPoint(j++, 182, 90);
                SetPathPoint(j++, 127, 90);
                SetPathPoint(j++, 74, 86);
                SetPathPoint(j++, 53, 96);
                SetPathPoint(j++, 40, 110);
                SetPathPoint(j++, 33, 132);
                SetPathPoint(j++, 40, 151);
                SetPathPoint(j++, 67, 164);
                SetPathPoint(j++, 119, 157);
                SetPathPoint(j++, 169, 155);
                SetPathPoint(j++, 215, 156);
                SetPathPoint(j++, 243, 166);
                SetPathPoint(j++, 255, 189);
                SetPathPoint(j++, 252, 206);
                SetPathPoint(j++, 233, 219);
                SetPathPoint(j++, 188, 220);
                SetPathPoint(j++, 143, 221);
                SetPathPoint(j++, 103, 221);
                SetPathPoint(j++, 77, 221);
                SetPathPoint(j++, 58, 242);
                SetPathPoint(j++, 55, 264);
                SetPathPoint(j++, 66, 277);
                SetPathPoint(j++, 88, 279);
                SetPathPoint(j++, 116, 284);
                SetPathPoint(j++, 154, 292);
                SetPathPoint(j++, 224, 289);
                SetPathPoint(j++, 277, 286);
                SetPathPoint(j++, 311, 286);
                SetPathPoint(j++, 355, 292);
                SetPathPoint(j++, 394, 291);
                SetPathPoint(j++, 466, 288);
                SetPathPoint(j++, 499, 288);
                SetPathPoint(j++, 538, 288);
                SetPathPoint(j++, 565, 281);
                SetPathPoint(j++, 582, 261);
                SetPathPoint(j++, 589, 237);
                SetPathPoint(j++, 572, 208);
                SetPathPoint(j++, 544, 204);
                SetPathPoint(j++, 513, 203);
                SetPathPoint(j++, 483, 203);
                SetPathPoint(j++, 438, 209);
                SetPathPoint(j++, 410, 202);
                SetPathPoint(j++, 393, 186);
                SetPathPoint(j++, 398, 160);
                SetPathPoint(j++, 418, 147);
                SetPathPoint(j++, 439, 142);
                SetPathPoint(j++, 465, 142);
                SetPathPoint(j++, 490, 143);
                SetPathPoint(j++, 514, 145);
                SetPathPoint(j++, 556, 148);
                SetPathPoint(j++, 585, 139);
                SetPathPoint(j++, 597, 115);
                SetPathPoint(j++, 598, 94);
                SetPathPoint(j++, 586, 77);
                SetPathPoint(j++, 556, 70);
                SetPathPoint(j++, 520, 65);
                SetPathPoint(j++, 484, 65);
                SetPathPoint(j++, 453, 66);
                SetPathPoint(j++, 379, 71);
                SetPathPoint(j++, 357, 58);
                SetPathPoint(j++, 344, 32);
                SetPathPoint(j++, 353, 9);
                SetPathPoint(j++, 387, -2);
                SetPathPoint(j++, 423, -4);
                SetPathPoint(j++, 461, -5);
                SetPathPoint(j++, 487, -2);
                SetPathPoint(j++, 515, 8);
            } else
            if(sub_level == 3)
            {
                fish_to_save_at_start = fish_to_save = 128;
                path_speed = 0.65;
                path_inc_x = 10;
                path_inc_y = 323;
                path_start_t = 15;
                num_path_points = 21;
                path = new PathPoint[num_path_points];
                SetPathPoint(j++, -40, 0);
                SetPathPoint(j++, 23, 0);
                SetPathPoint(j++, 139, 0);
                SetPathPoint(j++, 270, 0);
                SetPathPoint(j++, 430, 0);
                SetPathPoint(j++, 505, 1);
                SetPathPoint(j++, 538, -3);
                SetPathPoint(j++, 548, -14);
                SetPathPoint(j++, 545, -28);
                SetPathPoint(j++, 536, -48);
                SetPathPoint(j++, 387, -264);
                SetPathPoint(j++, 365, -284);
                SetPathPoint(j++, 342, -286);
                SetPathPoint(j++, 324, -280);
                SetPathPoint(j++, 296, -256);
                SetPathPoint(j++, 98, -79);
                SetPathPoint(j++, 80, -62);
                SetPathPoint(j++, 83, -42);
                SetPathPoint(j++, 104, -36);
                SetPathPoint(j++, 370, -70);
                SetPathPoint(j++, 390, -60);
            } else
            if(sub_level == 4)
            {
                fish_to_save_at_start = fish_to_save = 150;
                path_speed = 0.8;
                path_inc_x = 67;
                path_inc_y = 0;
                path_start_t = 6;
                num_path_points = 58;
                path = new PathPoint[num_path_points];
                SetPathPoint(j++, 0, 0);
                SetPathPoint(j++, 0, 8);
                SetPathPoint(j++, -14, 51);
                SetPathPoint(j++, 2, 62);
                SetPathPoint(j++, 32, 69);
                SetPathPoint(j++, 57, 69);
                SetPathPoint(j++, 95, 69);
                SetPathPoint(j++, 129, 69);
                SetPathPoint(j++, 321, 66);
                SetPathPoint(j++, 409, 67);
                SetPathPoint(j++, 469, 71);
                SetPathPoint(j++, 501, 75);
                SetPathPoint(j++, 512, 93);
                SetPathPoint(j++, 495, 113);
                SetPathPoint(j++, 406, 112);
                SetPathPoint(j++, 340, 105);
                SetPathPoint(j++, 267, 105);
                SetPathPoint(j++, 138, 109);
                SetPathPoint(j++, 18, 106);
                SetPathPoint(j++, -12, 124);
                SetPathPoint(j++, -8, 159);
                SetPathPoint(j++, 30, 168);
                SetPathPoint(j++, 94, 169);
                SetPathPoint(j++, 425, 160);
                SetPathPoint(j++, 461, 157);
                SetPathPoint(j++, 487, 157);
                SetPathPoint(j++, 508, 161);
                SetPathPoint(j++, 510, 189);
                SetPathPoint(j++, 488, 203);
                SetPathPoint(j++, 448, 204);
                SetPathPoint(j++, 56, 213);
                SetPathPoint(j++, 6, 210);
                SetPathPoint(j++, -17, 221);
                SetPathPoint(j++, -28, 236);
                SetPathPoint(j++, -20, 253);
                SetPathPoint(j++, -2, 256);
                SetPathPoint(j++, 452, 246);
                SetPathPoint(j++, 502, 248);
                SetPathPoint(j++, 515, 257);
                SetPathPoint(j++, 522, 280);
                SetPathPoint(j++, 510, 302);
                SetPathPoint(j++, 451, 303);
                SetPathPoint(j++, 341, 300);
                SetPathPoint(j++, 247, 302);
                SetPathPoint(j++, 144, 301);
                SetPathPoint(j++, 66, 299);
                SetPathPoint(j++, 12, 294);
                SetPathPoint(j++, -9, 297);
                SetPathPoint(j++, -35, 315);
                SetPathPoint(j++, -38, 336);
                SetPathPoint(j++, -23, 349);
                SetPathPoint(j++, 27, 351);
                SetPathPoint(j++, 103, 351);
                SetPathPoint(j++, 200, 351);
                SetPathPoint(j++, 314, 351);
                SetPathPoint(j++, 401, 343);
                SetPathPoint(j++, 460, 341);
                SetPathPoint(j++, 488, 357);
            } else
            if(sub_level == 5)
            {
                fish_to_save_at_start = fish_to_save = 150;
                path_speed = 0.4;
                path_inc_x = 0;
                path_inc_y = 60;
                path_start_t = 24;
                num_path_points = 48;
                path = new PathPoint[num_path_points];
                SetPathPoint(j++, -30, 0);
                SetPathPoint(j++, 41, 0);
                SetPathPoint(j++, 123, -2);
                SetPathPoint(j++, 193, -2);
                SetPathPoint(j++, 239, 0);
                SetPathPoint(j++, 304, 1);
                SetPathPoint(j++, 355, 1);
                SetPathPoint(j++, 439, 6);
                SetPathPoint(j++, 530, 13);
                SetPathPoint(j++, 545, 26);
                SetPathPoint(j++, 556, 81);
                SetPathPoint(j++, 553, 149);
                SetPathPoint(j++, 559, 220);
                SetPathPoint(j++, 552, 260);
                SetPathPoint(j++, 509, 275);
                SetPathPoint(j++, 417, 293);
                SetPathPoint(j++, 258, 288);
                SetPathPoint(j++, 161, 286);
                SetPathPoint(j++, 83, 267);
                SetPathPoint(j++, 60, 235);
                SetPathPoint(j++, 44, 192);
                SetPathPoint(j++, 50, 143);
                SetPathPoint(j++, 60, 83);
                SetPathPoint(j++, 92, 57);
                SetPathPoint(j++, 138, 59);
                SetPathPoint(j++, 223, 59);
                SetPathPoint(j++, 316, 54);
                SetPathPoint(j++, 403, 61);
                SetPathPoint(j++, 456, 67);
                SetPathPoint(j++, 485, 93);
                SetPathPoint(j++, 494, 132);
                SetPathPoint(j++, 494, 172);
                SetPathPoint(j++, 475, 200);
                SetPathPoint(j++, 446, 220);
                SetPathPoint(j++, 415, 222);
                SetPathPoint(j++, 348, 223);
                SetPathPoint(j++, 269, 223);
                SetPathPoint(j++, 194, 209);
                SetPathPoint(j++, 142, 190);
                SetPathPoint(j++, 123, 144);
                SetPathPoint(j++, 140, 112);
                SetPathPoint(j++, 167, 105);
                SetPathPoint(j++, 192, 104);
                SetPathPoint(j++, 230, 109);
                SetPathPoint(j++, 258, 122);
                SetPathPoint(j++, 279, 135);
                SetPathPoint(j++, 305, 140);
                SetPathPoint(j++, 329, 140);
            }
            path_speed += (double)episode * 0.08;
            bubbles = new Bubble[128];
            for(int k = 0; k < 128; k++)
                bubbles[k] = new Bubble();

            path_t0 = 0;
            path_last_t = 0;
            for(int l = 0; l < num_path_points; l++)
                path_last_t += path[l].dist_to_next;

            path_last_t /= 26;
            first_bub = new Bubble();
            first_bub.bm = GetRandomBubble();
            first_bub.fish_inside = true;
            Point2D.Double pnt = new Point2D.Double();
            GetPathPoint(pnt, 0);
            first_bub.x = pnt.x;
            first_bub.y = pnt.y;
            next_bubble = GetRandomBubble();
            next_bubble2 = GetRandomBubble();
            totally_random_bubble = false;
        }

        boolean ExistsInPath(krBitmap krbitmap)
        {
            for(Bubble bubble = first_bub; bubble != null; bubble = bubble.next)
                if(bubble.bm == krbitmap)
                    return true;

            return false;
        }

        public krBitmap GetRandomBubble()
        {
            int i = 3 + (episode + 1) / 2;
            if(i > 7)
                i = 7;
            if(totally_random_bubble)
                return bm_bubbles[(int)((double)i * Math.random())];
            int j = 0;
            krBitmap krbitmap;
            do
            {
                krbitmap = bm_bubbles[(int)((double)i * Math.random())];
                if(j++ > 300)
                {
                    System.out.print("stalled.\n");
                    return krbitmap;
                }
            } while(!ExistsInPath(krbitmap));
            return krbitmap;
        }

        public void UpdateBubble(Bubble bubble)
        {
            GetPathPoint(tp, path_t0 + bubble.t);
            bubble.x = tp.x;
            bubble.y = tp.y;
        }

        Bubble GetLastBubble()
        {
            if(first_bub == null)
                return null;
            Bubble bubble;
            for(bubble = first_bub; bubble.next != null; bubble = bubble.next);
            return bubble;
        }

        Bonus GetRandomBonus()
        {
            int i = (int)(Math.random() * (double)num_bonuses);
            if(i >= num_bonuses)
                i = num_bonuses - 1;
            Bonus bonus = bonuses;
            for(bonus = bonuses; i != 0; bonus = bonus.next)
                i--;

            return bonus;
        }

        public int randi(int i)
        {
            return (int)(Math.random() * (double)i);
        }

        public boolean AdjIsGoingToBurst(Bubble bubble)
        {
            krBitmap krbitmap = bubble.bm;
            int i = 1;
            for(Bubble bubble1 = bubble.prev; bubble1 != null && bubble1.bm == krbitmap; bubble1 = bubble1.prev)
                i++;

            for(Bubble bubble2 = bubble.next; bubble2 != null && bubble2.bm == krbitmap; bubble2 = bubble2.next)
                i++;

            return i >= 3;
        }

        public void UpdateBubbles(double f)
        {
            double f1 = path_speed * 1.5;
            Bubble bubble = GetLastBubble();
            if(bubble != null)
            {
                if(game_starting)
                {
                    double f2 = path_t0 + bubble.t;
                    if(f2 < path_start_t - 8)
                        f1 *= 25;
                    else
                    if(f2 < path_start_t)
                    {
                        f1 *= 1 + (24 * (path_start_t - f2)) / 8;
                    } else
                    {
                        game_starting = false;
                    }
                }
                double f3 = (path_t0 + bubble.t) / path_last_t;
                if(time_paused <= 0)
                    if(f3 < 0.7)
                    {
                        if(f3 > 0.1)
                            handicap += 0.1 * (0.7 - f3) * f;
                        else
                            handicap += 0.06 * f;
                    } else
                    if(f3 > 0.7)
                        handicap -= 0.15 * (f3 - 0.7) * f;
                if(handicap < 0.95)
                    handicap = 0.95;
                else
                if(handicap > 4)
                    handicap = 4;
                f1 *= handicap;
                if(f3 < 0.4)
                {
                    f3 = 1 + 15 * (0.4 - f3);
                    f1 *= f3;
                } else
                if(f3 > 0.8)
                {
                    if(f3 > 0.95)
                        f3 = 0.95;
                    f3 = 2.5 * (1 - f3);
                    if(f3 < 0.15)
                        f3 = 0.15;
                    f1 *= f3;
                } else
                {
                    f3 = 0.5 + 0.5 * (1 - (f3 - 0.4) / 0.4);
                    f1 *= f3;
                }
            }
            if(f1 < 0.2)
                f1 = 0.2;
            if(time_paused > 0)
            {
                double f4 = 1;
                if(time_paused > 5)
                    f4 = 1 - (6 - time_paused);
                else
                if(time_paused > 1)
                    f4 = 0;
                else
                if(time_paused > 0)
                    f4 = 1 - time_paused;
                f1 *= f4;
            }
            if(time_rewind > 0)
            {
                double f5 = 0;
                if(time_rewind > 3)
                    f5 = 4 - time_rewind;
                else
                if(time_rewind > 1)
                    f5 = 1;
                else
                if(time_rewind > 0)
                    f5 = 1 * time_rewind;
                f1 = f1 * (1 - f5) + -3 * f5;
            }
            if(game_over)
            {
                f1 += go_speedup;
                go_speedup += 32 * f;
            } else
            if(f1 > 12)
                f1 = 12;
            else
            if(f1 < -12)
                f1 = -12;
            f1 *= f;
            if(bubble != null && (path_t0 + bubble.t > 0 || f1 > 0))
                path_t0 += f1;
            Bubble bubble1 = first_bub;
            if(game_over)
            {
                if(first_bub == null)
                    st_timer += f;
                if(st_timer > 1)
                {
                    game_state = 3;
                }
            }
            if(level_completed)
            {
                st_timer += f;
                if(st_timer > 1)
                {
                    if(game_state != 2)
                        karu.PlaySound(snd_lev_comp);
                    game_state = 2;
                }
                return;
            }
            double f6 = shot_bubble.y;
            shot_bubble.y -= 620 * f;
            if(shot_bubble.bm != null)
            {
                for(double f7 = shot_bubble.y; f7 < f6; f7 += 5)
                {
                    PartBub partbub = new PartBub(shot_bubble.x, f7, bm_part_bub[0], 3 + (double)(1.2 * Math.random()));
                    partbub.vx = 20 * (double)(Math.random() - 0.5);
                    partbub.vy = -40 * (double)Math.random();
                    partbub.x += 13 * (double)(Math.random() - 0.5);
                    AddPart(partbub);
                }

            }
            if(shot_bubble.y < -26)
                shot_bubble.bm = null;
            while(!game_over && fish_to_save > 0 && first_bub.x > -26) 
            {
                bubble1 = new Bubble();
                bubble1.phase = first_bub.phase - 0.1;
                bubble1.fish_inside = true;
                bubble1.shot = false;
                first_bub.prev = bubble1;
                bubble1.next = first_bub;
                bubble1.t = first_bub.t - 1;
                totally_random_bubble = true;
                bubble1.bm = GetRandomBubble();
                totally_random_bubble = false;
                first_bub = bubble1;
                UpdateBubble(first_bub);
            }
            for(; bubble1 != null; bubble1 = bubble1.next)
            {
                if(path_t0 + bubble1.t >= path_last_t)
                {
                    if(!game_over)
                        karu.PlaySound(snd_bob_loses);
                    game_over = true;
                    if(bubble1.prev != null)
                        bubble1.prev.next = null;
                    if(bubble1 == first_bub)
                    {
                        first_bub = null;
                        return;
                    }
                }
                bubble1.phase += f;
                if(bubble1.phase >= 1)
                    bubble1.phase--;
                bubble1.trans -= 4 * f;
                if(bubble1.trans < 0)
                    bubble1.trans = 0;
                UpdateBubble(bubble1);
                if(shot_bubble.bm != null && shot_bubble.y - 26 <= bubble1.y + 8 && f6 + 26 + 10 > bubble1.y + 8 && Math.abs(shot_bubble.x - bubble1.x) < 15)
                {
                    Bubble bubble2 = new Bubble();
                    bubble2.bm = shot_bubble.bm;
                    bubble2.shot = true;
                    bubble2.trans = 1;
                    bubble2.attach_x = shot_bubble.x;
                    bubble2.attach_y = shot_bubble.y;
                    double f10 = 0;
                    if(bubble1.prev != null)
                        f10 = Math.min(bubble1.prev.x, bubble1.x);
                    else
                        f10 = bubble1.x - 13;
                    double f11 = bubble1.x;
                    if(bubble1.prev != null)
                        f11 = Math.max(bubble1.prev.x, bubble1.x);
                    double f12 = 1;
                    double f15 = 0;
                    if(bubble1.prev != null)
                    {
                        f12 = bubble1.prev.x - bubble1.x;
                        f15 = bubble1.prev.y - bubble1.y;
                    } else
                    if(bubble1.next != null)
                    {
                        f12 = bubble1.x - bubble1.next.x;
                        f15 = bubble1.y - bubble1.next.y;
                    }
                    double f16 = (double)Math.sqrt(f12 * f12 + f15 * f15);
                    f15 /= f16;
                    boolean flag2 = true;
                    if(Math.abs(f15) > 0.4)
                    {
                        if(f15 < 0)
                            flag2 = true;
                        else
                            flag2 = false;
                    } else
                    if((bubble1.prev != null && shot_bubble.x > f10 || bubble1.prev == null) && shot_bubble.x < f11)
                        flag2 = false;
                    else
                        flag2 = true;
                    if(!flag2)
                    {
                        bubble2.next = bubble1;
                        bubble2.prev = bubble1.prev;
                        bubble2.t = bubble1.t - 0.5;
                        if(bubble1.prev != null)
                            bubble1.prev.next = bubble2;
                        else
                            first_bub = bubble2;
                        bubble1.prev = bubble2;
                    } else
                    {
                        bubble2.prev = bubble1;
                        bubble2.next = bubble1.next;
                        bubble2.t = bubble1.t + 0.5;
                        if(bubble1.next != null)
                            bubble1.next.prev = bubble2;
                        bubble1.next = bubble2;
                    }
                    shot_bubble.bm = null;
                }
                if(bubble1.next != null)
                {
                    int i = 1;
                    boolean flag = bubble1.shot;
                    if(bubble1.prev == null || bubble1.prev.bm != bubble1.bm)
                    {
                        for(Bubble bubble3 = bubble1.next; bubble3 != null && bubble3.bm == bubble1.bm; bubble3 = bubble3.next)
                        {
                            if(bubble3.t - ((double)i + bubble1.t) > 0.01)
                            {
                                i = 0;
                                break;
                            }
                            if(bubble3.shot)
                                flag = true;
                            i++;
                        }

                    }
                    if(flag && i >= 3)
                    {
                        karu.PlaySound(snd_plip_plop);
                        level_score += i * 50;
                        total_score += i * 50;
                        if(!IsSomeBonusActing() && (randi(16) == 4 || i >= 4 && randi(10) <= i))
                        {
                            double f13 = (path_t0 + bubble.t) / path_last_t;
                            if(f13 > 0.4)
                            {
                                Item item = new Item();
                                item.type = 3;
                                item.x = bubble1.x;
                                item.y = item.py = bubble1.y;
                                item.vel_y = 70;
                                item.bonus = GetRandomBonus();
                                item.bm = item.bonus.icon;
                                if(items == null)
                                {
                                    items = item;
                                } else
                                {
                                    item.next = items.next;
                                    items.next = item;
                                }
                            }
                        }
                        boolean flag1 = false;
                        int j = 1;
                        double f17 = 0;
                        double f20 = 0;
                        for(int k = i; k > 0; k--)
                        {
                            f17 += bubble1.x;
                            f20 += bubble1.y;
                            j += bubble1.combo;
                            if(bubble1.fish_inside)
                            {
                                total_fish_saved++;
                                fish_to_save--;
                                Part part = new Part(bubble1.x, bubble1.y, bm_smfish, 0.4);
                                part.vx = (double)(-140 + Math.random() * -40);
                                part.vy = (double)((Math.random() - 0.5) * 40);
                                AddPart(part);
                            }
                            SpawnBurst(bubble1.x, bubble1.y);
                            if(bubble1.next != null)
                                bubble1.next.prev = bubble1.prev;
                            if(bubble1.prev != null)
                                bubble1.prev.next = bubble1.next;
                            if(bubble1 == first_bub)
                                first_bub = bubble1.next;
                            if(bubble1.next != null)
                            {
                                bubble1 = bubble1.next;
                            } else
                            {
                                flag1 = true;
                                bubble1 = bubble1.prev;
                            }
                        }

                        f17 /= i;
                        f20 /= i;
                        if(bubble1 != null && AdjIsGoingToBurst(bubble1))
                            bubble1.combo = (byte)j;
                        if(j > 1)
                        {
                            PartString partstring = new PartString(f17, f20, null, 0.6);
                            partstring.vx = 0;
                            partstring.vy = -30;
                            partstring.str = "Combo " + Integer.toString(j) + "x";
                            AddPart(partstring);
                        }
                        if(longest_combo < j)
                            longest_combo = j;
                        if(j > 0)
                            j--;
                        if(j > 7)
                            j = 7;
                        karu.PlaySound(snd_combo[j]);
                        Bubble bubble6 = bubble1;
                        if(bubble6 != null && bubble6.prev != null && bubble6.bm == bubble6.prev.bm && !flag1)
                            bubble6.shot = true;
                    }
                    if(bubble1 == null)
                    {
                        if(fish_to_save <= 0)
                            level_completed = true;
                        if(level_completed)
                        {
                            first_bub = null;
                            return;
                        } else
                        {
                            first_bub = bubble1 = new Bubble();
                            bubble1.bm = GetRandomBubble();
                            bubble1.t = -path_t0;
                            UpdateBubble(bubble1);
                            return;
                        }
                    }
                    if(bubble1.next == null)
                        return;
                    double f14 = Math.abs(bubble1.next.t - bubble1.t);
                    if(f14 < 0.99)
                    {
                        for(Bubble bubble4 = bubble1.next; bubble4 != null; bubble4 = bubble4.next)
                        {
                            double f18 = 6 * (1 - f14) * f;
                            if(f18 > f14)
                                f18 = f14;
                            bubble4.t += f18;
                        }

                    }
                    if(f14 > 1.01)
                    {
                        for(Bubble bubble5 = bubble1.next; bubble5 != null; bubble5 = bubble5.next)
                        {
                            double f19 = 2 * f14 * f;
                            if(f19 > 0.15)
                                f19 = 0.15;
                            if(f19 > f14)
                                f19 = f14;
                            bubble5.t -= f19;
                        }

                    }
                }
                if(bubble1.combo > 0 && !AdjIsGoingToBurst(bubble1))
                    bubble1.combo = 0;
            }

        }

        public void DrawPath()
        {
            int i = 0;
            int j = 0;
            Point2D.Double pnt = new Point2D.Double();
            Bubble bubble = first_bub;
            double f2 = 0;
            for(; bubble != null; bubble = bubble.next)
            {
                if(path_t0 + bubble.t < path_last_t - 1)
                {
                    if(bubble.fish_inside)
                        karu.DrawBitmap(bm_smfish, (int)bubble.x - 8, ((int)bubble.y - 6) + (int)(2 * Math.sin((double)(bubble.phase * 2) * Math.PI)));
                    double f3 = 1 - bubble.trans;
                    i = (int)(bubble.trans * bubble.attach_x + f3 * bubble.x);
                    if(path_t0 + bubble.t >= path_last_t - 4)
                    {
                        double f4 = ((path_t0 + bubble.t) - (path_last_t - 4)) / 4;
                        j = (int)((double)(bubble.trans * bubble.attach_y + f3 * bubble.y) + 3 * Math.sin((double)(bubble.phase * 2) * Math.PI) * (double)(1 - f4) + 4 * Math.sin(2 * Math.PI * (double)akey2) * (double)f4);
                    } else
                    {
                        j = (int)((double)(bubble.trans * bubble.attach_y + f3 * bubble.y) + 3 * Math.sin((double)(bubble.phase * 2) * Math.PI));
                    }
                    karu.DrawBitmap(bubble.bm, i - 13, j - 13);
                }
                f2 = bubble.t;
            }

            f2 += path_t0 + 1;
            f2 += st_timer * ((path_t0 + path_last_t) - f2);
            f2 = (int)f2;
            for(double f1 = (int)f2; f1 < path_last_t; f1 += 0.5)
            {
                GetPathPoint(pnt, f1);
                int k = (int)((1 + Math.sin((double)akey2 * Math.PI * 2)) * 2);
                if(k >= 4)
                    k = 3;
                if(k < 0)
                    k = 0;
                j = (int)((double)pnt.y + 3 * Math.sin(2 * Math.PI * (double)akey1 + (double)f1));
                karu.DrawBitmap(bm_part_bub[3], (int)pnt.x - 2, j - 2);
            }

            i = (int)(path[num_path_points - 1].x - 55);
            i = (int)((double)i + (1 - Math.sin(0.5 * Math.PI + 0.5 * Math.PI * (double)st_timer)) * (double)(480 - i));
            j = (int)path[num_path_points - 1].y;
            karu.DrawBitmap(bm_evil_fish, i, (j + (int)(4 * Math.sin(2 * Math.PI * (double)akey2))) - 40);
        }

        public boolean Init(Karu karu1)
        {
            karu = karu1;
            int i = 0;
            items = null;
            fnt = Font.decode("Arial-BOLD-16");
            fnt2 = Font.decode("Arial-BOLD-20");
            fnt3 = Font.decode("Arial-PLAIN-14");
            fnts = new Font[11];
            for(i = 0; i < 11; i++)
                fnts[i] = Font.decode("Arial-PLAIN-" + Integer.toString(i + 2));

            Part part = new Part(10, 10, null, 0);
            for(i = 0; i < 100; i++)
            {
                part.next = new Part(10, 10, null, 0);
                part = part.next;
            }

            for(i = 0; i < 100; i++)
            {
                part.next = new PartBub(10, 10, null, 0);
                part = part.next;
            }

            bm_loading = karu1.GetBitmap("resources/loading.jpg");
            i = 0;
            bm_menub_u = new krBitmap[12];
            bm_menub_d = new krBitmap[12];
            bm_menub_u[0] = karu1.GetBitmap("resources/btn_play_again_u.png");
            bm_menub_d[0] = karu1.GetBitmap("resources/btn_play_again_d.png");
            bm_click_to_continue = karu1.GetBitmap("resources/click_to_continue.png");
            bm_congrats = karu1.GetBitmap("resources/congrats.png");
            bm_bob = new krBitmap[11];
            bm_bob[0] = karu1.GetBitmap("resources/bob_0000.png");
            bm_bob[1] = karu1.GetBitmap("resources/bob_0002.png");
            bm_bob[2] = karu1.GetBitmap("resources/bob_0004.png");
            bm_bob[3] = karu1.GetBitmap("resources/bob_0006.png");
            bm_bob[4] = karu1.GetBitmap("resources/bob_0008.png");
            bm_bob[5] = karu1.GetBitmap("resources/bob_0008.png");
            bm_bob[6] = karu1.GetBitmap("resources/bob_0010.png");
            bm_bob[7] = karu1.GetBitmap("resources/bob_0012.png");
            bm_bob[8] = karu1.GetBitmap("resources/bob_0014.png");
            bm_bob[9] = karu1.GetBitmap("resources/bob_0016.png");
            bm_bob[10] = karu1.GetBitmap("resources/bob_0018.png");
            bm_evil_fish = karu1.GetBitmap("resources/evil_fish.png");
            bm_smfish = karu1.GetBitmap("resources/smfish.png");
            snd_shoot_bubble = karu1.GetSound("resources/release_bubble.au");
            karu1.GetSound("resources/release_bubble.au");
            snd_combo = new krSound[16];
            snd_combo[0] = karu1.GetSound("resources/combo_01.au");
            snd_combo[1] = karu1.GetSound("resources/combo_02.au");
            snd_combo[2] = karu1.GetSound("resources/combo_03.au");
            snd_combo[3] = karu1.GetSound("resources/combo_04.au");
            snd_combo[4] = karu1.GetSound("resources/combo_05.au");
            snd_combo[5] = karu1.GetSound("resources/combo_06.au");
            snd_combo[6] = karu1.GetSound("resources/combo_07.au");
            snd_combo[7] = karu1.GetSound("resources/combo_08.au");
            snd_plip_plop = karu1.GetSound("resources/pop_01.au");
            snd_pop = karu1.GetSound("resources/pop_01.au");
            snd_bob_loses = karu1.GetSound("resources/bob_loses.au");
            snd_pick = karu1.GetSound("resources/pickup.au");
            snd_swap = karu1.GetSound("resources/gulp.au");
            snd_lev_comp = karu1.GetSound("resources/lev_comp_song.au");
            i = 0;
            bm_bubbles = new krBitmap[8];
            bm_bubbles[i++] = karu1.GetBitmap("resources/blue.png");
            bm_bubbles[i++] = karu1.GetBitmap("resources/red.png");
            bm_bubbles[i++] = karu1.GetBitmap("resources/green.png");
            bm_bubbles[i++] = karu1.GetBitmap("resources/orange.png");
            bm_bubbles[i++] = karu1.GetBitmap("resources/purple.png");
            bm_bubbles[i++] = karu1.GetBitmap("resources/cyan.png");
            bm_bubbles[i++] = karu1.GetBitmap("resources/white.png");
            num_bubbles = i;
            bm_bg_game = karu1.GetBitmap("resources/seagrass.jpg");
            bm_bg_menu = karu1.GetBitmap("resources/bg_menu.jpg");
            bm_lev_comp = karu1.GetBitmap("resources/lev_comp.png");
            bm_game_over = karu1.GetBitmap("resources/game_over.png");
            snd_level_start = karu1.GetSound("resources/level_start.au");
            snd_explosion = karu1.GetSound("resources/explosion.au");
            bm_part_bub = new krBitmap[4];
            bm_part_bub[0] = karu1.GetBitmap("resources/part_bub_01.png");
            bm_part_bub[1] = karu1.GetBitmap("resources/part_bub_02.png");
            bm_part_bub[2] = karu1.GetBitmap("resources/part_bub_03.png");
            bm_part_bub[3] = karu1.GetBitmap("resources/part_bub_04.png");
            bm_torpedo = karu1.GetBitmap("resources/torpedo.png");
            bm_smrock = karu1.GetBitmap("resources/smrock.png");
            bm_fisho = karu1.GetBitmap("resources/fisho_full.png");
            bonuses = new BonusPause("Pause", karu1.GetBitmap("resources/bonus_pause.png"));
            bonuses.next = new BonusRewind("Rewind", karu1.GetBitmap("resources/bonus_rewind.png"));
            bonuses.next.next = new BonusTorpedo("Torpedo", karu1.GetBitmap("resources/bon_torpedo.png"));
            bonuses.next.next.next = new BonusSmRocks("Small Rocks", karu1.GetBitmap("resources/bon_smrocks.png"));
            num_bonuses = 4;
            InitLevel(1);
            game_state = 0;
            go_speedup = 0;
            level_completed = false;
            st_timer = 0;
            return true;
        }

        public void DrawMenu()
        {
            char c = '\244';
            char c1 = '\334';
            int i = GetMenuSel(karu.mouse_x, karu.mouse_y);
            for(int j = 0; j < bm_menub_d.length; j++)
                    if(i == j)
                        karu.DrawBitmap(bm_menub_d[j], c, c1 + j * 28);
                    else
                        karu.DrawBitmap(bm_menub_u[j], c, c1 + j * 28);
        }

        public int GetMenuSel(int i, int j)
        {
            char c = '\244';
            char c1 = '\334';
            for(int k = 0; k < bm_menub_d.length; k++)
                if(i >= c && j >= c1 && i <= c + 151 && j <= c1 + k * 28 + 25)
                    return k;
            return -1;
        }

        public String ScoreString()
        {
            StringBuffer stringbuffer = new StringBuffer(Integer.toString(score_show));
            for(int i = stringbuffer.length() - 1 - 2; i > 0; i -= 3)
                stringbuffer.insert(i, " ");

            return stringbuffer.toString();
        }

        public void DrawBar()
        {
            int i = bm_fisho.img.getWidth(null);
            int j = (int)((double)i * ((double)(fish_to_save_at_start - fish_to_save) / (double)fish_to_save_at_start));
            karu.gr.drawImage(bm_fisho.img, 276, 342, 276 + j, 342 + bm_fisho.img.getHeight(null), 0, 0, j, bm_fisho.img.getHeight(null), null);
            karu.gr.setColor(Color.white);
            karu.gr.setFont(fnt);
            karu.gr.drawString(ScoreString(), 63, 354);
            karu.gr.drawString(Integer.toString(episode + 1) + " - " + Integer.toString(sub_level), 190, 354);
        }

        public void Draw()
        {
            if(karu.IsLoading())
            {
                karu.gr.setColor(Color.black);
                karu.gr.fillRect(0, 0, 480, 360);
                karu.DrawBitmap(bm_loading, 178, 156);
                return;
            }
            if(game_state == 0)
            {
                karu.DrawBitmap(bm_bg_menu, 0, 0);
                paused = false;
            } else
            if(game_state == 2)
            {
                paused = false;
                karu.DrawBitmap(bm_bg_game, 0, 0);
                karu.DrawBitmap(bm_lev_comp, 90, 30);
                karu.gr.setColor(Color.white);
                karu.gr.setFont(fnt);
                karu.gr.setFont(fnt);
                DrawOutlined(340, 151, "Fish Saved:");
                DrawOutlined(340, 168, Integer.toString(total_fish_saved));
                DrawOutlined(140, 151, "Max Combo:");
                DrawOutlined(140, 168, Integer.toString(longest_combo));
                DrawOutlined(240, 205, "Level Score:");
                DrawOutlined(240, 222, Integer.toString(level_score));
                karu.gr.setFont(fnt);
                karu.gr.setColor(Color.white);
                karu.DrawBitmap(bm_click_to_continue, 156, 290);
                DrawParts();
                DrawBar();
            } else
            if(game_state == 3)
            {
                paused = false;
                karu.DrawBitmap(bm_bg_game, 0, 0);
                if(completed_the_game)
                {
                    karu.gr.setColor(Color.white);
                    karu.gr.setFont(fnt2);
                    karu.DrawBitmap(bm_congrats, 50, 30);
                    karu.gr.setColor(Color.white);
                    karu.gr.setFont(fnt);
                    DrawOutlined(340, 175, "Fish Saved:");
                    DrawOutlined(340, 192, Integer.toString(total_fish_saved));
                    DrawOutlined(140, 175, "Final Score:");
                    DrawOutlined(140, 192, Integer.toString(total_score));
                } else
                {
                    karu.DrawBitmap(bm_game_over, 132, 30);
                    karu.gr.setColor(Color.white);
                    karu.gr.setFont(fnt);
                    DrawOutlined(240, 111, "Fish Saved:");
                    DrawOutlined(240, 128, Integer.toString(total_fish_saved));
                    DrawOutlined(240, 155, "Final Score:");
                    DrawOutlined(240, 172, Integer.toString(total_score));
                }
                DrawParts();
                DrawMenu();
                DrawBar();
            } else
            if(game_state == 1)
            {
                karu.DrawBitmap(bm_bg_game, 0, 0);
                DrawPath();
                DrawParts();
                DrawItems();
                int i = (int)(5 * Math.sin((double)shoot_time * Math.PI));
                bob_x = karu.mouse_x;
                if(torpedo)
                    karu.DrawBitmap(bm_torpedo, karu.mouse_x - bm_torpedo.img.getWidth(null) / 2, (bob_y - 0 - bm_torpedo.img.getHeight(null) / 2) + i);
                else
                if(smrocks > 0)
                {
                    karu.DrawBitmap(bm_smrock, karu.mouse_x - bm_smrock.img.getWidth(null) / 2, (bob_y - 0 - bm_smrock.img.getHeight(null) / 2) + i);
                } else
                {
                    if(shoot_time >= 1)
                    {
                        karu.DrawBitmap(next_bubble, karu.mouse_x - 13, (bob_y - 15) + i);
                    } else
                    {
                        int j = (int)(shoot_time * 2 * 13);
                        karu.gr.drawImage(next_bubble.img, karu.mouse_x - j / 2, (int)((((double)bob_y + (1 - shoot_time) * 4 * 13) - 15) + (double)i), j, j, null);
                    }
                    byte byte0 = 16;
                    karu.gr.drawImage(next_bubble2.img, (karu.mouse_x - byte0 / 2) + 1, (int)((double)((bob_y - 4) + i) + (1 - shoot_time) * 16), byte0, byte0, null);
                }
                int k = (int)(bob_akey * 11);
                if(k > 10)
                    k = 10;
                if(k < 0)
                    k = 0;
                karu.DrawBitmap(bm_bob[k], karu.mouse_x - 22, bob_y + i);
                DrawBar();
                if(name_show < 1 && !paused)
                {
                    karu.gr.setFont(fnt2);
                    karu.gr.setColor(Color.black);
                    String s1 = new String("Level " + Integer.toString(episode + 1) + "-" + Integer.toString(sub_level));
                    DrawOutlined(240, 170, s1);
                }
            }
            if(paused)
            {
                karu.gr.setFont(fnt2);
                karu.gr.setColor(Color.black);
                DrawOutlined(240, 170, "Paused");
                karu.gr.setFont(fnt);
                karu.gr.setColor(Color.black);
                karu.DrawTextCentered(240, 190, "Press space to continue");
            }
        }

        public void DrawOutlined(int i, int j, String s)
        {
            karu.gr.setColor(Color.black);
            karu.DrawTextCentered(i - 2, j - 2, s);
            karu.DrawTextCentered(i - 2, j + 2, s);
            karu.DrawTextCentered(i + 2, j + 2, s);
            karu.DrawTextCentered(i + 2, j - 2, s);
            karu.DrawTextCentered(i - 2, j, s);
            karu.DrawTextCentered(i, j + 2, s);
            karu.DrawTextCentered(i + 2, j, s);
            karu.DrawTextCentered(i, j - 2, s);
            karu.gr.setColor(new Color(0xecd300));
            karu.DrawTextCentered(i, j, s);
        }

        public void Frame(double f)
        {
            if(karu == null)
                return;
            if(karu.IsLoading())
                return;
            tim++;
            if(paused)
                return;
            if(!level_completed && !game_over && !ExistsInPath(next_bubble))
                next_bubble = GetRandomBubble();
            akey0 += f;
            if(akey0 >= 1)
                akey0--;
            akey1 += 0.7 * f;
            if(akey1 >= 1)
                akey1--;
            akey2 += 0.5 * f;
            if(akey2 >= 1)
                akey2--;
            akey3 += 0.3 * f;
            if(akey3 >= 1)
                akey3--;
            UpdateParts(f);
            time_paused -= f;
            if(time_paused < 0)
                time_paused = 0;
            time_rewind -= f;
            if(time_rewind < 0)
                time_rewind = 0;
            shoot_time += 3 * f;
            if(shoot_time > 1)
                shoot_time = 1;
            if(game_state != 1)
                return;
            name_show += 0.7 * f;
            int i = (int)(5 * f * (double)(total_score - score_show));
            if(i < 4)
                i = 4;
            score_show += i;
            if(score_show > total_score)
                score_show = total_score;
            UpdateItems(f);
            UpdateBubbles(f);
            for(bob_akey += f; bob_akey >= 1; bob_akey--);
        }

        public void focusGained(FocusEvent focusevent)
        {
        }

        public void focusLost(FocusEvent focusevent)
        {
            paused = true;
        }

        Karu karu;
        final String BUILD = new String("0016");
        krBitmap bm_bob[];
        krBitmap bm_menub_u[];
        krBitmap bm_menub_d[];
        krBitmap bm_bubbles[];
        double bob_akey;
        krBitmap bm_evil_fish;
        krBitmap bm_smfish;
        krBitmap bm_torpedo;
        krBitmap bm_smrock;
        krBitmap bm_fisho;
        krBitmap bm_click_to_continue;
        krBitmap bm_congrats;
        krBitmap bm_bg_menu;
        krBitmap bm_bg_game;
        krBitmap bm_lev_comp;
        krBitmap bm_game_over;
        krBitmap bm_part_bub[];
        krSound snd_level_start;
        krSound snd_explosion;
        krSound snd_shoot_bubble;
        krSound snd_plip_plop;
        krSound snd_pop;
        krSound snd_bob_loses;
        krSound snd_pick;
        krSound snd_swap;
        krSound snd_lev_comp;
        krSound snd_combo[];
        krBitmap bm_loading;
        Font fnt;
        Font fnt2;
        Font fnt3;
        Font fnts[];
        boolean torpedo;
        int smrocks;
        double name_show;
        int num_bubbles;
        int episode;
        int sub_level;
        int total_fish_saved;
        int longest_combo;
        double handicap;
        int game_state;
        boolean game_starting;
        boolean completed_the_game;
        final int RAD_BUBBLE = 13;
        final int W_SMFISH = 8;
        final int H_SMFISH = 6;
        final int GS_MAINMENU = 0;
        final int GS_PLAY = 1;
        final int GS_LEVEL_COMPLETED = 2;
        final int GS_GAME_OVER = 3;
        final int ITEM_FREE = 0;
        final int ITEM_BUBBLE = 1;
        final int ITEM_TORPEDO = 2;
        final int ITEM_BONUS = 3;
        final int ITEM_SMROCKS = 4;
        final double MAX_TIME_PAUSED = 6;
        final double MAX_TIME_REWIND = 4;
        double time_rewind;
        double time_paused;
        int num_bonuses;
        Bonus bonuses;
        Part parts;
        Item items;
        int num_path_points;
        PathPoint path[];
        double path_t0;
        double path_speed;
        int fish_to_save;
        int fish_to_save_at_start;
        double path_last_t;
        double path_start_t;
        final int MAX_BUBBLES = 128;
        Bubble bubbles[];
        Bubble first_bub;
        double akey0;
        double akey1;
        double akey2;
        double akey3;
        int bob_y;
        int bob_x;
        double shoot_time;
        int level;
        Point2D.Double tp;
        Bubble shot_bubble;
        krBitmap next_bubble;
        krBitmap next_bubble2;
        boolean game_over;
        double go_speedup;
        boolean level_completed;
        double st_timer;
        int level_score;
        int total_score;
        int score_show;
        boolean paused;
        int path_inc_x;
        int path_inc_y;
        boolean totally_random_bubble;
        int tim;
    }


    public void init()
    {
        ProgBob progbob = new ProgBob();
        karu = new Karu(this, progbob, 480, 360);
        addFocusListener(progbob);
        base_time = System.currentTimeMillis();
        current_time = System.currentTimeMillis();
    }

    public void update(Graphics g)
    {
        frames++;
        current_time = System.currentTimeMillis();
        double f = (double)(current_time - base_time) * 0.001;
        if(f > 0.1)
            f = 0.1;
        double f1;
        for(f1 = f; f1 > 0.02; f1 -= 0.02)
            karu.prog.Frame(0.02);

        if(f1 > 0.001)
            karu.prog.Frame(f1);
        base_time = current_time;
        karu.prog.Draw();
        g.drawImage(karu.backbuffer, 0, 0, null);
        paint(g);
        Thread.currentThread().setPriority(10);
    }


    Karu karu;
    long base_time;
    long current_time;
    long frames;
    double fps_timer;
}
