diff --git a/apps/plugins/SUBDIRS b/apps/plugins/SUBDIRS
index c497c49..0cd3e2c 100644
--- a/apps/plugins/SUBDIRS
+++ b/apps/plugins/SUBDIRS
@@ -88,3 +88,7 @@ mpegplayer
 #if PLUGIN_BUFFER_SIZE >= 0x80000
 lua
 #endif
+
+#if defined(COWON_D2) || defined(IPOD_VIDEO) || defined(CREATIVE_ZVM60GB) || defined(CREATIVE_ZVM30GB)
+bomberman
+#endif
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index 65b1a38..9724ea6 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -914,4 +914,26 @@ resistor.128x128x16.bmp
 resistor.68x20x16.bmp
 #endif
 
+#if defined(COWON_D2) || defined(IPOD_VIDEO) || defined(CREATIVE_ZVM60GB) || defined(CREATIVE_ZVM30GB)
+bomberman_player.16x24x16.bmp
+bomberman_box.96x16x16.bmp
+bomberman_block.16x16x16.bmp
+bomberman_bomb.48x16x16.bmp
+bomberman_explode.64x144x16.bmp
+bomberman_player_move.64x96x16.bmp
+bomberman_ai1_move.64x96x16.bmp
+bomberman_ai2_move.64x96x16.bmp
+bomberman_ai3_move.64x96x16.bmp
+bomberman_ai4_move.64x96x16.bmp
+bomberman_player_death.64x24x16.bmp
+bomberman_player_win.32x24x16.bmp
+bomberman_ai1_win.32x24x16.bmp
+bomberman_ai2_win.32x24x16.bmp
+bomberman_ai3_win.32x24x16.bmp
+bomberman_ai4_win.32x24x16.bmp
+bomberman_bonus.16x64x16.bmp
+bomberman_gameover.320x240x16.bmp
+bomberman_youwon.320x240x16.bmp
+#endif
+
 #endif /* HAVE_LCD_BITMAP */
diff --git a/apps/plugins/bomberman/SOURCES b/apps/plugins/bomberman/SOURCES
new file mode 100644
index 0000000..0c1a8c6
--- /dev/null
+++ b/apps/plugins/bomberman/SOURCES
@@ -0,0 +1,4 @@
+bomberman.c
+game.c
+draw.c
+ai.c
diff --git a/apps/plugins/bomberman/ai.c b/apps/plugins/bomberman/ai.c
new file mode 100644
index 0000000..698ec77
--- /dev/null
+++ b/apps/plugins/bomberman/ai.c
@@ -0,0 +1,554 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2010-2011 Lev Panov, Nick Petrov
+ * 
+ * Bomberman plugin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+
+#include "game.h"
+
+#define USE_PATH_CACHE 0
+#if USE_PATH_CACHE
+#define PATH_CACHE_UPD_TIME 50
+#endif /* USE_PATH_CACHE */
+
+#define MAX_PATH_LEN 50
+#define UNREAL_F 999
+#define PATH_OFFSET 2
+#define MOVE_COST 10
+
+struct node_t {
+    bool IsWalkable;
+    bool IsOnOpen;
+    bool IsOnClose;
+    int G, H, F;
+    int ParentX, ParentY;
+};
+
+struct path_elem {
+    int X, Y;
+};
+
+struct path_t {
+    struct path_elem Path[MAP_W * MAP_H];
+    int Distance;
+};
+
+struct ai_vars {
+    int ClosestPlayer;
+    bool Danger;
+    struct path_elem SafetyPlace;
+    int Bombs;
+
+#if USE_PATH_CACHE
+    struct path_t PathCache;
+    unsigned int PathCacheUpdTime;
+#endif /* USE_PATH_CACHE */
+};
+
+static struct node_t Nodes[MAP_W][MAP_H];
+static struct ai_vars AI[MAX_PLAYERS];
+
+inline static bool GetNode(struct field_t *field, int x, int y, bool UseBoxes)
+{
+    return (field->map[x][y] == SQUARE_FREE || (UseBoxes && field->map[x][y] == SQUARE_BOX));
+}
+
+static void InitNodes(struct field_t *F, bool UseBoxes)
+{
+    int x, y;
+
+    for (x = 0; x < MAP_W; x++)
+        for (y = 0; y < MAP_H; y++)
+        {
+	    Nodes[x][y].IsWalkable = GetNode(F, x, y, UseBoxes);
+	    Nodes[x][y].IsOnOpen = false;
+	    Nodes[x][y].IsOnClose = false;
+	    Nodes[x][y].G = 0;
+	    Nodes[x][y].H = 0;
+	    Nodes[x][y].F = 0;
+	    Nodes[x][y].ParentX = 0;
+	    Nodes[x][y].ParentY = 0;
+	}
+}
+
+static int FindPath(struct game_t *G, struct path_t *Path, int StartX, int StartY,
+                                         int EndX, int EndY, bool UseBoxes)
+{
+    int x = 0, y = 0; /* for running through the nodes */
+    int dx, dy; /* for the 8 (4) squares adjacent to each node */
+    int cx = StartX, cy = StartY;
+    int lowestf = UNREAL_F; /* start with the lowest being the highest */
+    int count = 0;
+
+#ifdef __DEBUG
+     int desc;
+     char logStr[100] = "\n";
+
+     if ((desc = rb->open(PLUGIN_GAMES_DIR "/path.txt", O_WRONLY | O_APPEND | O_CREAT, 0666)) < 0)
+     {
+       rb->splash(HZ, "Cant open");
+       return;
+     }
+     rb->write(desc, logStr, rb->strlen(logStr));
+     rb->memset(logStr, 0, 100);
+     rb->snprintf(logStr, 5, "%i %i\n", n, m);
+     rb->write(desc, logStr, rb->strlen(logStr));
+#endif /* #ifdef __DEBUG */
+
+    InitNodes(&G->field, UseBoxes);
+    /* add starting node to open list */
+    Nodes[StartX][StartY].IsOnOpen = true;
+    Nodes[StartX][StartY].IsOnClose = false;
+
+    while (cx != EndX || cy != EndY)
+    {
+	count++;
+	
+        if (count > MAX_PATH_LEN)
+	{
+#ifdef __DEBUG
+            rb->memset(logStr, 0, 100);
+            rb->snprintf(logStr, 8, "%s\n", "Return");
+            rb->write(desc, logStr, rb->strlen(logStr));
+#endif /* #ifdef __DEBUG */
+
+            return 0;
+        }
+
+        /* look for lowest F cost node on open list - this becomes the current node */
+	lowestf = UNREAL_F;
+	for (x = 0; x < MAP_W; x++)
+	{
+            for (y = 0; y < MAP_H; y++)
+            {
+		Nodes[x][y].F = Nodes[x][y].G + Nodes[x][y].H;
+		if (Nodes[x][y].IsOnOpen)
+		{
+                    if (Nodes[x][y].F < lowestf)
+                    {
+                        cx = x;
+                        cy = y;
+                        lowestf = Nodes[x][y].F;
+                    }
+		}
+            }
+	}
+	
+	/* we found it, so now put that node on the closed list */
+	Nodes[cx][cy].IsOnOpen = false;
+	Nodes[cx][cy].IsOnClose = true;
+	
+	/* for each of the 8 (4) adjacent node */
+	for (dx = -1; dx <= 1; dx++)
+	{
+            for (dy = -1; dy <= 1; dy++)
+            {
+		/* we don't want to walk by diagonals in bomberman */
+		if (dx != -dy && dx != dy)
+		{
+                    if ((cx + dx) < MAP_W && (cx + dx) > -1 &&
+                        (cy + dy) < MAP_H && (cy + dy) > -1)
+                    {
+                        /* if its walkable and not on the closed list */
+                        if (Nodes[cx + dx][cy + dy].IsWalkable == true
+				&& Nodes[cx + dx][cy + dy].IsOnClose == false)
+                        {
+			    /* if its not on open list */
+                            if (Nodes[cx + dx][cy + dy].IsOnOpen == false)
+                            {
+                                /* add it to open list */
+                                Nodes[cx + dx][cy + dy].IsOnOpen = true;
+                                Nodes[cx + dx][cy + dy].IsOnClose = false;
+                                /* make the current node its parent */
+                                Nodes[cx + dx][cy + dy].ParentX = cx;
+                                Nodes[cx + dx][cy + dy].ParentY = cy;
+
+#ifdef __DEBUG
+                                rb->memset(logStr, 0, 100);
+                                rb->snprintf(logStr, 21, "%i C: %i %i P: %i %i\n",
+                                             n, cx + dx, cy + dy, cx, cy);
+                                rb->write(desc, logStr, rb->strlen(logStr));
+#endif /* #ifdef __DEBUG */
+
+                                /* work out G */
+                                Nodes[cx + dx][cy + dy].G = MOVE_COST; /* straights cost 10 */
+                                /* work out H */
+                                /* MANHATTAN METHOD */
+                                Nodes[cx + dx][cy + dy].H =
+                                        (abs(EndX - (cx + dx)) +
+                                         abs(EndY - (cy + dy))) * MOVE_COST;
+                                Nodes[cx + dx][cy + dy].F =
+                                        Nodes[cx + dx][cy + dy].G +
+                                        Nodes[cx + dx][cy + dy].H;
+                            }
+			} /* end if walkable and not on closed list */
+		    }
+		}
+            }
+	}
+    }
+
+    /* follow all the parents back to the start */
+    cx = EndX;
+    cy = EndY;
+    Path->Distance = 0;
+    Path->Path[Path->Distance].X = cx;
+    Path->Path[Path->Distance].Y = cy;
+    Path->Distance++;
+
+    while (cx != StartX || cy != StartY)
+    {
+	Path->Path[Path->Distance].X = Nodes[cx][cy].ParentX;
+	Path->Path[Path->Distance].Y = Nodes[cx][cy].ParentY;
+	cx = Path->Path[Path->Distance].X;
+	cy = Path->Path[Path->Distance].Y;
+	
+#ifdef __DEBUG
+        rb->memset(logStr, 0, 100);
+        rb->snprintf(logStr, 12 , "%i PP: %i %i\n", n, Path->Path[Path->Distance].X,
+                      Path->Path[Path->Distance].Y);
+        rb->write(desc, logStr, rb->strlen(logStr));
+#endif /* #ifdef __DEBUG */
+	
+	Path->Distance++;
+        if (Path->Distance > MAX_PATH_LEN)
+            return 0;
+    }
+
+#ifdef __DEBUG
+    rb->close(desc);
+#endif /* #ifdef __DEBUG */
+
+    return 1;
+}
+
+#ifdef __DEBUG
+void LogPath(struct path_t *P)
+{
+    int file;
+    int i;
+    char logStr[100] = "\n";
+
+    if ((file = rb->open(PLUGIN_GAMES_DIR "/safe_path.txt", 
+                         O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0)
+    {
+	return;
+    }
+    
+    rb->write(file, logStr, 100);
+    
+    for (i = 0; i < P->Distance; i++)
+    {
+	rb->memset(logStr, 0, 100);
+	rb->snprintf(logStr, 7, "%i %i\n", P->Path[i].X, P->Path[i].Y);
+	rb->write(file, logStr, 100);
+    }
+
+    rb->close(file);
+}
+#endif /* #ifdef __DEBUG */
+
+static int FoundDangerBombs(struct game_t *G, int x, int y)
+{
+    int i, j;
+    int ProtectWalls;
+    bool Checked, BombPlaced;
+
+    if (x < 0 || y < 0 || x >= MAP_W || y >= MAP_H)
+        return -1;
+
+    for (i = 0; i < MAX_BOMBS; i++)
+    {
+        ProtectWalls = 0;
+        Checked = false;
+        BombPlaced = false;
+        if (G->field.bombs[i].state >= BOMB_PLACED)
+        {
+            BombPlaced = true;
+
+            if (G->field.bombs[i].ypos == y)
+            {
+                Checked = true;
+
+                if (abs(G->field.bombs[i].xpos - x) > G->bomb_rad[G->field.bombs[i].power])
+                    ProtectWalls++;
+                else if (G->field.bombs[i].xpos < x)
+                {
+                    for (j = G->field.bombs[i].xpos; j < x; j++)
+                    {
+                        if (G->field.map[j][y] == SQUARE_BLOCK ||
+                            G->field.map[j][y] == SQUARE_BOX)
+                        {
+                            ProtectWalls++;
+                            break;
+                        }
+                    }
+                }
+                else if (G->field.bombs[i].xpos > x)
+                {
+                    for (j = x; j < G->field.bombs[i].xpos; j++)
+                    {
+                        if (G->field.map[j][y] == SQUARE_BLOCK ||
+                            G->field.map[j][y] == SQUARE_BOX)
+                        {
+                            ProtectWalls++;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            /* else ? */
+            if (G->field.bombs[i].xpos == x)
+            {
+                Checked = true;
+
+                if (abs(G->field.bombs[i].ypos - y) > G->bomb_rad[G->field.bombs[i].power])
+                    ProtectWalls++;
+                else if (G->field.bombs[i].ypos < y)
+                {
+                    for (j = G->field.bombs[i].ypos; j < y; j++)
+                    {
+                        if (G->field.map[x][j] == SQUARE_BLOCK ||
+                            G->field.map[x][j] == SQUARE_BOX)
+                        {
+                            ProtectWalls++;
+                            break;
+                        }
+                    }
+                }
+                else if (G->field.bombs[i].ypos > y)
+                {
+                    for (j = y; j < G->field.bombs[i].ypos; j++)
+                    {
+                        if (G->field.map[x][j] == SQUARE_BLOCK ||
+                            G->field.map[x][j] == SQUARE_BOX)
+                        {
+                            ProtectWalls++;
+                            break;
+                        }
+                    }
+                }
+            }
+
+            if (ProtectWalls >= 1 || (!Checked && BombPlaced))
+                continue;
+
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+inline static bool IsPlayerNearPlayer(struct game_t *G, struct player_t *P1, struct player_t *P2)
+{
+    if (P1->xpos == P2->xpos)
+    {
+        if (abs(P1->ypos - P2->ypos) <= G->bomb_rad[P1->power])
+            return true;
+    }
+    else if (P1->ypos == P2->ypos)
+    {
+        if (abs(P1->xpos - P2->xpos) <= G->bomb_rad[P1->power])
+            return true;
+    }
+
+    return false;
+}
+
+static bool FindSafetyPlace(struct game_t *G, struct ai_vars *P,  struct path_t *Path, struct player_t *Pl)
+{
+    int dx, dy;
+    int MinDist = UNREAL_F;
+    struct path_elem Tmp;
+    int x = Pl->xpos, y = Pl->ypos;
+
+    Tmp.X = 0;
+    Tmp.Y = 0;
+    for (dx = -MAP_W; dx <= MAP_W; dx++)
+        for (dy = -MAP_H; dy <= MAP_H; dy++)
+        {
+            if ((x + dx) < MAP_W && (x + dx) > -1 &&
+                (y + dy) < MAP_H && (y + dy) > -1)
+            {
+                if (G->field.map[x + dx][y + dy] == SQUARE_FREE)
+                {
+                    if (FoundDangerBombs(G, x + dx, y + dy) == 0)
+                    {
+                        if (FindPath(G, Path, x, y, x + dx, y + dy, false))
+                        {
+                            if (Path->Distance < MinDist)
+                            {
+                                MinDist = Path->Distance;
+                                Tmp.X = x + dx;
+                                Tmp.Y = y + dy;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+    if (MinDist < UNREAL_F)
+    {
+        P->SafetyPlace.X = Tmp.X;
+        P->SafetyPlace.Y = Tmp.Y;
+        return true;
+    }
+
+    return false;
+}
+
+inline static bool IsABox(struct game_t *G, struct path_elem *P)
+{
+    return (G->field.map[P->X][P->Y] == SQUARE_BOX);
+}
+
+inline static void MovePlayer(struct game_t *G, struct player_t *P, struct path_t *Path)
+{
+    if (Path->Distance > 1)
+    {
+        int index = Path->Distance - PATH_OFFSET; /* index is positive */
+        if (P->xpos < Path->Path[index].X)
+	    PlayerMoveRight(G, P);
+        else if (P->xpos > Path->Path[index].X)
+	    PlayerMoveLeft(G, P);
+        else if (P->ypos < Path->Path[index].Y)
+	    PlayerMoveDown(G, P);
+        else if (P->ypos > Path->Path[index].Y)
+	    PlayerMoveUp(G, P);  
+    }
+}
+
+inline static int CheckFire(struct game_t *G, int x, int y)
+{
+    return G->field.firemap[x][y] & BITMASK_ALL_DIRS;
+}
+
+void UpdateAI(struct game_t *G, struct player_t *Players)
+{
+    int i, j;
+    bool isDanger;
+    struct path_t Path, PathToClosestPlayer;
+    int MinDist = UNREAL_F;
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+        if (Players[i].isAI && !Players[i].ismove && Players[i].status.state == ALIVE)
+        {
+#if USE_PATH_CACHE
+            if (get_tick() - AI[i].PathCacheUpdTime < PATH_CACHE_UPD_TIME)
+            {
+                MovePlayer(G, &Players[i], &AI[i].PathCache);
+                if (AI[i].PathCache.Distance > PATH_OFFSET)
+                    AI[i].PathCache.Distance--;
+                //rb->splash(HZ/20, "USE CACHE!");
+                return;
+            }
+#endif /* USE_PATH_CACHE */
+
+            isDanger = false;
+            MinDist = UNREAL_F;
+
+            if (FoundDangerBombs(G, Players[i].xpos, Players[i].ypos) == 1)
+                isDanger = true;
+
+            if (!isDanger)
+            {
+                AI[i].Danger = false;
+                for (j = 0; j < MAX_PLAYERS; j++)
+                {
+                    if (j == i || Players[j].status.state > ALIVE)
+                        continue;
+
+                    FindPath(G, &Path, Players[i].xpos, Players[i].ypos,
+                                       Players[j].xpos, Players[j].ypos, true);
+
+                    if (Path.Distance < MinDist)
+                    {
+                        MinDist = Path.Distance;
+                        memcpy(&PathToClosestPlayer, &Path, sizeof(struct path_t));
+                        AI[i].ClosestPlayer = j;
+                    }
+                }
+            }
+            else /* Danger */
+            {
+                if (FindSafetyPlace(G, &AI[i], &Path, &Players[i]))
+                    AI[i].Danger = true;
+                else
+                  continue;
+            }
+
+            if (AI[i].Danger)
+            {
+                if (FoundDangerBombs(G, Players[i].xpos, Players[i].ypos) == 1)
+                    FindSafetyPlace(G, &AI[i], &Path, &Players[i]);
+
+                if (FindPath(G, &Path, Players[i].xpos, Players[i].ypos,
+                             AI[i].SafetyPlace.X, AI[i].SafetyPlace.Y, false))
+                {
+                    MovePlayer(G, &Players[i], &Path);
+
+#if USE_PATH_CACHE
+                    /* create path cache */
+                    rb->memcpy(&AI[i].PathCache, &Path, sizeof(struct path_t));
+                    if (AI[i].PathCache.Distance > PATH_OFFSET)
+                        AI[i].PathCache.Distance--;
+                    AI[i].PathCacheUpdTime = get_tick();
+#endif /* USE_PATH_CACHE */
+                }
+            }
+            else if (IsPlayerNearPlayer(G, &Players[i], &Players[AI[i].ClosestPlayer]))
+            {
+                PlayerPlaceBomb(G, &Players[i]);
+            }
+            else
+            {
+                int index = Path.Distance - PATH_OFFSET;
+                if (index < 0) index = 0;
+                else if (index >= MAP_W * MAP_H) index = 0;
+                if (!CheckFire(G, Path.Path[index].X, Path.Path[index].Y) &&
+                        !Players[i].bombs_placed)
+                {
+                    if (IsABox(G, &Path.Path[index]) && AI[i].Danger == false)
+                    {
+                        PlayerPlaceBomb(G, &Players[i]);
+                    }
+                    else
+                    {
+                        MovePlayer(G, &Players[i], &PathToClosestPlayer);
+
+#if USE_PATH_CACHE
+                        /* create path cache */
+                        rb->memcpy(&AI[i].PathCache, &PathToClosestPlayer, sizeof(struct path_t));
+                        if (AI[i].PathCache.Distance > PATH_OFFSET)
+                            AI[i].PathCache.Distance--;
+                        AI[i].PathCacheUpdTime = get_tick();
+#endif /* USE_PATH_CACHE */
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/apps/plugins/bomberman/ai.h b/apps/plugins/bomberman/ai.h
new file mode 100644
index 0000000..bc9c874
--- /dev/null
+++ b/apps/plugins/bomberman/ai.h
@@ -0,0 +1,29 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2010-2011 Lev Panov, Nick Petrov
+ * 
+ * Bomberman plugin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _AI_H
+#define _AI_H
+
+void UpdateAI(struct game_t *G, struct player_t *Players);
+
+#endif /* _AI_H */
diff --git a/apps/plugins/bomberman/bomberman.c b/apps/plugins/bomberman/bomberman.c
new file mode 100644
index 0000000..a585176
--- /dev/null
+++ b/apps/plugins/bomberman/bomberman.c
@@ -0,0 +1,535 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2010-2011 Lev Panov, Nick Petrov
+ * 
+ * Bomberman plugin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+#include "lib/pluginlib_actions.h"
+#include "lib/pluginlib_exit.h"
+
+#include "lib/helper.h"
+#include "lib/display_text.h"
+#include "lib/highscore.h"
+#include "lib/playback_control.h"
+
+#include "game.h"
+#include "draw.h"
+#include "ai.h"
+
+const struct button_mapping *plugin_contexts[] = {
+    pla_main_ctx,
+    #if defined(HAVE_REMOTE_LCD)
+    pla_remote_ctx,
+    #endif
+};
+
+#define NB_ACTION_CONTEXTS \
+    (sizeof(plugin_contexts) / sizeof(struct button_mapping*))
+
+/*
+ * Files.
+ */
+
+#define SAVE_FILE  PLUGIN_GAMES_DIR "/bomberman.save"
+#define SCORE_FILE PLUGIN_GAMES_DIR "/bomberman.score"
+
+/*
+ * Some globals.
+ */
+
+unsigned long tick = 0;
+static struct game_t game;
+static bool resume = false;
+static bool resume_file = false;
+
+#define NUM_SCORES 5
+static struct highscore highscores[NUM_SCORES];
+
+/*
+ * Main code.
+ */
+
+void InitGame(struct game_t *game)
+{
+    int i, j;
+
+    /*int DefaultMap[MAP_H][MAP_W] = {
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},
+        {2,0,0,1,1,1,0,1,0,1,0,1,0,1,0,0,2},
+        {2,0,2,1,2,1,2,0,2,1,2,0,2,1,2,0,2},
+        {2,0,0,1,1,1,1,1,0,1,0,1,0,1,0,0,2},
+        {2,0,2,1,2,1,2,0,2,1,2,0,2,1,2,0,2},
+        {2,0,0,1,1,1,1,1,0,1,0,1,0,1,0,0,2},
+        {2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2},
+        {2,0,0,1,1,1,1,1,0,1,0,1,0,1,0,0,2},
+        {2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}
+        };*/
+    /*int DefaultMap[MAP_H][MAP_W] = {
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},
+        {2,0,0,1,1,1,0,1,0,1,0,1,0,1,0,0,2},
+        {2,0,2,1,1,1,1,1,1,1,1,1,1,1,2,0,2},
+        {2,0,1,0,0,0,0,0,0,1,0,1,0,1,1,0,2},
+        {2,0,1,1,2,1,2,0,0,1,2,0,2,1,1,0,2},
+        {2,0,1,1,1,1,1,1,0,0,0,0,0,1,1,0,2},
+        {2,0,1,0,0,0,0,0,2,1,2,0,2,0,1,0,2},
+        {2,0,0,0,1,1,1,0,0,0,0,0,0,1,1,1,2},
+        {2,0,2,1,1,1,1,1,1,1,1,1,1,1,2,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}
+        };*/
+    /*int DefaultMap[MAP_H][MAP_W] = {
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},
+        {2,0,0,0,1,1,0,1,0,1,0,1,1,0,0,0,2},
+        {2,0,2,0,1,1,1,1,1,1,1,1,1,0,2,0,2},
+        {2,1,1,1,1,0,0,0,0,1,0,1,1,1,1,1,2},
+        {2,0,1,1,2,1,2,0,0,1,2,0,2,1,1,0,2},
+        {2,0,1,1,1,1,1,1,0,0,0,0,0,1,1,0,2},
+        {2,0,1,0,0,0,0,0,2,1,2,0,2,0,1,0,2},
+        {2,1,1,1,1,1,1,0,0,0,0,0,1,1,1,1,2},
+        {2,0,2,0,1,1,1,1,1,1,1,1,1,0,2,0,2},
+        {2,0,0,0,1,1,0,0,0,0,0,0,1,0,0,0,2},
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}
+        };*/
+    int DefaultMap[MAP_H][MAP_W] = {
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},
+        {2,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,2},
+        {2,0,2,1,2,1,2,1,2,1,2,1,2,1,2,0,2},
+        {2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2},
+        {2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2},
+        {2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2},
+        {2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2},
+        {2,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2},
+        {2,0,2,1,2,1,2,1,2,1,2,1,2,1,2,0,2},
+        {2,0,0,1,1,1,1,1,1,1,1,1,1,1,0,0,2},
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}
+        };
+    /*int DefaultMap[MAP_H][MAP_W] = {
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,2,0,2,0,2,0,2,0,2,0,2,0,2,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}
+        };*/
+    /*int DefaultMap[MAP_H][MAP_W] = {
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2},
+        {2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2}
+        };
+        */
+
+    for (i = 0; i < MAP_W; i++)
+        for (j = 0; j < MAP_H; j++)
+        {
+            game->field.map[i][j] = DefaultMap[j][i];
+            game->field.boxes[i][j].state = HUNKY;
+            game->field.bonuses[i][j] = BONUS_NONE;
+        }
+
+    for (i = 0; i < MAX_BOMBS; i++)
+        game->field.bombs[i].state = BOMB_NONE;
+
+    game->nplayers = MAX_PLAYERS;
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+        game->draw_order[i] = &game->players[i];
+
+    /* Set radius of explosion for each bomb power. */
+    game->bomb_rad[BOMB_PWR_SINGLE] = 1;
+    game->bomb_rad[BOMB_PWR_DOUBLE] = 2;
+    game->bomb_rad[BOMB_PWR_TRIPLE] = 4;
+    game->bomb_rad[BOMB_PWR_QUAD]   = 6;
+    game->bomb_rad[BOMB_PWR_KILLER] = MAX(MAP_W, MAP_H);
+
+    /* Set player maximum move phase for each speed. */
+    game->max_move_phase[0] = 5;
+    game->max_move_phase[1] = 2;
+    game->max_move_phase[2] = 1;
+
+    /* Place bonuses. */
+    int nboxes = 0, nbonuses;
+    for (i = 0; i < MAP_W; i++)
+        for (j = 0; j < MAP_H; j++)
+            if (game->field.map[i][j] == SQUARE_BOX)
+                nboxes++;
+    /* Not all boxes consist a bonus. */
+    nbonuses = nboxes / 2;
+    while (nbonuses)
+    {
+        i = rb->rand() % MAP_W;
+        j = rb->rand() % MAP_H;
+
+        if (game->field.map[i][j] == SQUARE_BOX && game->field.bonuses[i][j] == BONUS_NONE)
+        {
+            /* Choose a random bonus for this box. */
+            game->field.bonuses[i][j] = rb->rand() % (BONUS_NONE);
+            nbonuses--;
+        }
+    }
+
+    /* Let the game begin! */
+    game->state = GAME_GAME;
+}
+
+void InitPlayer(struct player_t *player, int num, int x, int y, bool isAI)
+{
+    player->status.state = ALIVE;
+    player->xpos = x;
+    player->ypos = y;
+    player->look = LOOK_DOWN;
+    player->speed = 0;
+    player->bombs_max = 1;
+    player->bombs_placed = 0;
+    player->power = BOMB_PWR_DOUBLE;
+    player->isFullPower = false;
+
+    player->rxpos = 0;
+    player->rypos = 0;
+    player->ismove = false;
+    player->move_phase = 0;
+
+    player->isAI = isAI;
+
+    player->num = num;
+}
+
+static void bomberman_loadgame(void)
+{
+    int fd;
+
+    resume = false;
+
+    /* Open game file. */
+    fd = rb->open(SAVE_FILE, O_RDONLY);
+    if (fd < 0) return;
+
+    /* Read in saved game. */
+    if((rb->read(fd, &game, sizeof(struct game_t)) <= 0))
+    {
+        rb->splash(HZ/2, "Failed to load game");
+    }
+    else
+    {
+        resume = true;
+    }
+
+    rb->close(fd);
+}
+
+static void bomberman_savegame(void)
+{
+    int fd;
+
+    /* Write out the game state to the save file. */
+    fd = rb->open(SAVE_FILE, O_WRONLY|O_CREAT, 0666);
+    if (fd < 0) return;
+
+    if ((rb->write(fd, &game, sizeof(struct game_t)) <= 0))
+    {
+        rb->close(fd);
+        rb->remove(SAVE_FILE);
+        rb->splash(HZ/2, "Failed to save game");
+        return;
+    }
+
+    rb->close(fd);
+}
+
+static int bomberman_help(void)
+{
+    static char *help_text[] = {
+        "Bomberman", "", "Aim", "",
+        "Destroy", "all", "the", "enemies", "by", "exploding", "bombs!"
+    };
+    static struct style_text formation[]={
+        { 0, TEXT_CENTER|TEXT_UNDERLINE },
+        { 2, C_RED },
+        { 19, C_RED },
+        { 37, C_RED },
+        { 39, C_BLUE },
+        { 46, C_RED },
+        { 52, C_GREEN },
+        { 59, C_ORANGE },
+        { 67, C_GREEN },
+        { 74, C_YELLOW },
+        { 80, C_RED },
+        LAST_STYLE_ITEM
+    };
+
+    rb->lcd_setfont(FONT_UI);
+#ifdef HAVE_LCD_COLOR
+    rb->lcd_set_background(LCD_BLACK);
+    rb->lcd_set_foreground(LCD_WHITE);
+#endif
+    if (display_text(ARRAYLEN(help_text), help_text, formation, NULL, true))
+        return 1;
+    rb->lcd_setfont(FONT_SYSFIXED);
+
+    return 0;
+}
+
+static int bomberman_menu_cb(int action, const struct menu_item_ex *this_item)
+{
+    int i = ((intptr_t)this_item);
+    if (action == ACTION_REQUEST_MENUITEM && !resume && (i == 0 || i == 5))
+        return ACTION_EXIT_MENUITEM;
+    return action;
+}
+
+static int bomberman_menu(void)
+{
+    int selected = 0;
+
+    MENUITEM_STRINGLIST(main_menu, "Bomberman Menu", bomberman_menu_cb,
+                        "Resume Game", "Start New Game",
+                        "Help", "High Scores",
+                        "Playback Control",
+                        "Quit without Saving", "Quit");
+
+    rb->button_clear_queue();
+    while (true) {
+        switch (rb->do_menu(&main_menu, &selected, NULL, false)) {
+            case 0: /* Resume Game */
+                if (resume_file)
+                    rb->remove(SAVE_FILE);
+                return 0;
+            case 1: /* Start New Game*/
+                InitGame(&game);
+                InitPlayer(&game.players[0], 0, 1, 1, false);
+                InitPlayer(&game.players[1], 1, 1, MAP_H - 3, true);
+                InitPlayer(&game.players[2], 2, MAP_W - 3, 1, true);
+                InitPlayer(&game.players[3], 3, MAP_W - 3, MAP_H - 2, true);
+                return 0;
+            case 2: /* Help */
+                if (bomberman_help())
+                    return 1;
+                break;
+            case 3: /* High Scores */
+                highscore_show(-1, highscores, NUM_SCORES, true);
+                break;
+            case 4: /* Playback Control */
+                if (playback_control(NULL))
+                    return 1;
+                break;
+            case 5: /* Quit without Saving */
+                return 1;
+            case 6: /* Quit */
+                if (resume) {
+                    rb->splash(HZ*1, "Saving game ...");
+                    bomberman_savegame();
+                }
+                return 1;
+            case MENU_ATTACHED_USB:
+                return 1;
+            default:
+                break;
+        }
+    }
+}
+
+static int bomberman_game_loop(void)
+{
+    int i;
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+    rb->cpu_boost(false);
+#endif
+
+    if (bomberman_menu())
+        return 1;
+
+    resume = false;
+    resume_file = false;
+
+#ifdef HAVE_ADJUSTABLE_CPU_FREQ
+    rb->cpu_boost(true);
+#endif
+
+    /* Main loop. */
+    while (true)
+    {
+        int end = get_tick() + HZ / CYCLETIME;
+
+        Draw(&game);
+
+        UpdateBombs(&game);
+        UpdateBoxes(&game);
+        UpdateAI(&game, game.players);
+
+        int action = pluginlib_getaction(TIMEOUT_NOBLOCK, plugin_contexts, NB_ACTION_CONTEXTS);
+        switch (action)
+        {
+            case PLA_CANCEL:
+            case PLA_EXIT:
+                resume = true;
+                return 0;
+
+            case PLA_UP:
+            case PLA_UP_REPEAT:
+                PlayerMoveUp(&game, &game.players[0]);
+                break;
+
+            case PLA_DOWN:
+            case PLA_DOWN_REPEAT:
+                PlayerMoveDown(&game, &game.players[0]);
+                break;
+
+            case PLA_RIGHT:
+            case PLA_RIGHT_REPEAT:
+                PlayerMoveRight(&game, &game.players[0]);
+                break;
+
+            case PLA_LEFT:
+            case PLA_LEFT_REPEAT:
+                PlayerMoveLeft(&game, &game.players[0]);
+                break;
+
+            case PLA_SELECT:
+                PlayerPlaceBomb(&game, &game.players[0]);
+                break;
+
+            default:
+                if (rb->default_event_handler(action) == SYS_USB_CONNECTED)
+                    return 1;
+                break;
+        }
+
+        if (game.state == GAME_GAME)
+        {
+            for (i = 0; i < MAX_PLAYERS; i++)
+            {
+                int upd = UpdatePlayer(&game, &game.players[i]);
+                if (upd == DEAD)
+                {
+                    game.nplayers--;
+                    if (game.nplayers == 1 || !game.players[i].isAI)
+                    {
+                        for (i = 0; i < MAX_PLAYERS; i++)
+                        {
+                            if (game.players[i].status.state == ALIVE) {
+                                game.players[i].status.state = WIN_PHASE1;
+                            }
+                        }
+                    }
+                }
+                else if (upd == -GAME_GAMEOVER || upd == -GAME_WON)
+                {
+                    game.state = -upd;
+                    tick = 0;
+                }
+            }
+        }
+
+        if (game.state == GAME_GAMEOVER || game.state == GAME_WON)
+            if (tick >= AFTERGAME_DUR)
+                return 0;
+
+        rb->yield();
+
+        if (TIME_BEFORE(get_tick(), end))
+            rb->sleep(end - get_tick());
+
+        tick++;
+    }
+}
+
+int main(void)
+{
+    highscore_load(SCORE_FILE, highscores, NUM_SCORES);
+
+    bomberman_loadgame();
+    resume_file = resume;
+
+    while (!bomberman_game_loop())
+    {
+        if (!resume)
+        {
+            int position = highscore_update(game.score, game.level + 1, "",
+                                            highscores, NUM_SCORES);
+            if (position != -1)
+            {
+                if (position == 0)
+                    rb->splash(HZ*2, "New High Score");
+                highscore_show(position, highscores, NUM_SCORES, true);
+            }
+            else
+            {
+                //brickmania_sleep(3);
+            }
+        }
+    }
+
+    highscore_save(SCORE_FILE, highscores, NUM_SCORES);
+
+    return PLUGIN_OK;
+}
+
+static void cleanup(void)
+{
+    /* This is handled by plugin api. Remove in future. */
+    /* rb->lcd_setfont(FONT_UI); */
+
+    /* Turn on backlight timeout (revert to settings) */
+    backlight_use_settings(); /* Backlight control in lib/helper.c */
+#ifdef HAVE_REMOTE_LCD
+    remote_backlight_use_settings();
+#endif
+}
+
+/* This is the plugin entry point. */
+enum plugin_status plugin_start(const void* parameter)
+{
+    int ret;
+
+    (void)parameter;
+    atexit(cleanup);
+
+    rb->lcd_setfont(FONT_SYSFIXED);
+#if LCD_DEPTH > 1
+    rb->lcd_set_backdrop(NULL);
+#endif
+    /* Turn off backlight timeout. */
+    backlight_force_on(); /* Backlight control in lib/helper.c */
+#ifdef HAVE_REMOTE_LCD
+    remote_backlight_force_on(); /* Remote backlight control in lib/helper.c */
+#endif
+
+    rb->srand(get_tick());
+
+    ret = main();
+
+    return ret;
+}
diff --git a/apps/plugins/bomberman/bomberman.make b/apps/plugins/bomberman/bomberman.make
new file mode 100644
index 0000000..06f7105
--- /dev/null
+++ b/apps/plugins/bomberman/bomberman.make
@@ -0,0 +1,46 @@
+#             __________               __   ___.
+#   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+#   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+#   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+#   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+#                     \/            \/     \/    \/            \/
+# $Id$
+#
+
+BOMBERMAN_SRCDIR := $(APPSDIR)/plugins/bomberman
+BOMBERMAN_BUILDDIR := $(BUILDDIR)/apps/plugins/bomberman
+
+BOMBERMAN_SRC := $(call preprocess, $(BOMBERMAN_SRCDIR)/SOURCES)
+BOMBERMAN_OBJ := $(call c2obj, $(BOMBERMAN_SRC))
+
+OTHER_SRC += $(BOMBERMAN_SRC)
+
+ifndef SIMVER
+ifneq (,$(strip $(foreach tgt,RECORDER ONDIO,$(findstring $(tgt),$(TARGET)))))
+    ### lowmem targets
+    ROCKS += $(BOMBERMAN_BUILDDIR)/bomberman.ovl
+    BOMBERMAN_OUTLDS = $(BOMBERMAN_BUILDDIR)/bomberman.link
+    BOMBERMAN_OVLFLAGS = -T$(BOMBERMAN_OUTLDS) -Wl,--gc-sections -Wl,-Map,$(basename $@).map
+else
+    ### all other targets
+    ROCKS += $(BOMBERMAN_BUILDDIR)/bomberman.rock
+endif
+else
+    ### simulator
+    ROCKS += $(BOMBERMAN_BUILDDIR)/bomberman.rock
+endif
+
+$(BOMBERMAN_BUILDDIR)/bomberman.rock: $(BOMBERMAN_OBJ)
+
+$(BOMBERMAN_BUILDDIR)/bomberman.refmap: $(BOMBERMAN_OBJ)
+
+$(BOMBERMAN_OUTLDS): $(PLUGIN_LDS) $(BOMBERMAN_BUILDDIR)/bomberman.refmap
+	$(call PRINTS,PP $(@F))$(call preprocess2file,$<,$@,-DOVERLAY_OFFSET=$(shell \
+		$(TOOLSDIR)/ovl_offset.pl $(BOMBERMAN_BUILDDIR)/bomberman.refmap))
+
+$(BOMBERMAN_BUILDDIR)/bomberman.ovl: $(BOMBERMAN_OBJ) $(BOMBERMAN_OUTLDS)
+	$(SILENT)$(CC) $(PLUGINFLAGS) -o $(basename $@).elf \
+		$(filter %.o, $^) \
+		$(filter %.a, $+) \
+		-lgcc $(BOMBERMAN_OVLFLAGS)
+	$(call PRINTS,LD $(@F))$(OC) -O binary $(basename $@).elf $@
diff --git a/apps/plugins/bomberman/draw.c b/apps/plugins/bomberman/draw.c
new file mode 100644
index 0000000..7ffc501
--- /dev/null
+++ b/apps/plugins/bomberman/draw.c
@@ -0,0 +1,313 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2010-2011 Lev Panov, Nick Petrov
+ *
+ * Bomberman plugin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+
+/* Bitmaps. */
+#include "pluginbitmaps/bomberman_player.h"
+#include "pluginbitmaps/bomberman_box.h"
+#include "pluginbitmaps/bomberman_block.h"
+#include "pluginbitmaps/bomberman_bomb.h"
+#include "pluginbitmaps/bomberman_explode.h"
+#include "pluginbitmaps/bomberman_player_move.h"
+#include "pluginbitmaps/bomberman_ai1_move.h"
+#include "pluginbitmaps/bomberman_ai2_move.h"
+#include "pluginbitmaps/bomberman_ai3_move.h"
+#include "pluginbitmaps/bomberman_ai4_move.h"
+#include "pluginbitmaps/bomberman_player_death.h"
+#include "pluginbitmaps/bomberman_player_win.h"
+#include "pluginbitmaps/bomberman_ai1_win.h"
+#include "pluginbitmaps/bomberman_ai2_win.h"
+#include "pluginbitmaps/bomberman_ai3_win.h"
+#include "pluginbitmaps/bomberman_ai4_win.h"
+#include "pluginbitmaps/bomberman_bonus.h"
+#include "pluginbitmaps/bomberman_gameover.h"
+#include "pluginbitmaps/bomberman_youwon.h"
+
+#include "game.h"
+#include "draw.h"
+
+#define SQUARE_SIZE 16
+#define XMAPOFFSET 25
+#define YMAPOFFSET 30
+
+/* Win bitmaps for each player number. */
+static const fb_data *win_bitmaps[5] = {bomberman_player_win,
+                                        bomberman_ai1_win,
+                                        bomberman_ai2_win,
+                                        bomberman_ai3_win,
+                                        bomberman_ai4_win};
+/* Move bitmaps for each player number. */
+static const fb_data *move_bitmaps[5] = {bomberman_player_move,
+                                         bomberman_ai1_move,
+                                         bomberman_ai2_move,
+                                         bomberman_ai3_move,
+                                         bomberman_ai4_move};
+
+/* Offsets (in pixels) for each position of player in one square. */
+//int xcoord[3] = {1, 6, 12};
+static const int xcoord[3] = {-4, 0, 4};
+//int ycoord[3] = {3, 9, 14};
+//int ycoord[3] = {1, 6, 12};
+//int ycoord[3] = {12, 6, 1};
+static const int ycoord[3] = {9, 3, -2};
+
+/* Some magic constants for right player's movement drawing. */
+static const int xcoord_k[4] = {0, 0, 1, -1};
+static const int ycoord_k[4] = {-1, 1, 0, 0};
+
+/* Drawing move phase for each actual move phase of player. */
+static const int move_cur_phases[6] = {0, 1, 2, 3, 2, 0};
+
+/* Binary offsets for each direction (used in explosion drawing) */
+static const int dir_offsets[5] = {0, 4, 8, 12, 16};
+
+/* Draw function. */
+void Draw(struct game_t *game)
+{
+    int i, j;
+
+    if (game->state == GAME_GAME)
+	rb->lcd_clear_display();
+
+    if (game->state == GAME_GAME)
+    {
+        /* Different objects on the field. */
+	for (i = 0; i < MAP_W; i++)
+            for (j = 0; j < MAP_H; j++)
+                switch (game->field.map[i][j])
+                {
+                case SQUARE_FREE:
+                    if (game->field.bonuses[i][j] != BONUS_NONE)
+                    {
+                        rb->lcd_bitmap_transparent_part(bomberman_bonus,
+                            0,
+                            game->field.bonuses[i][j] * SQUARE_SIZE,
+                            STRIDE(SCREEN_MAIN,
+                                   BMPWIDTH_bomberman_bonus,
+                                   BMPHEIGHT_bomberman_bonus),
+                            i * SQUARE_SIZE + XMAPOFFSET,
+                            j * SQUARE_SIZE + YMAPOFFSET,
+                            SQUARE_SIZE,
+                            SQUARE_SIZE);
+                    }
+                    break;
+                case SQUARE_BOX:
+                    /*
+                    rb->lcd_bitmap_transparent(bomberman_box,
+                                               i * SQUARE_SIZE + XMAPOFFSET,
+                                               j * SQUARE_SIZE + YMAPOFFSET,
+                                               BMPWIDTH_bomberman_box,
+                                               BMPHEIGHT_bomberman_box);
+                    */
+                    rb->lcd_bitmap_transparent_part(bomberman_box,
+                        game->field.boxes[i][j].state * SQUARE_SIZE,
+                        0,
+                        STRIDE(SCREEN_MAIN,
+                               BMPWIDTH_bomberman_box,
+                               BMPHEIGHT_bomberman_box),
+                        i * SQUARE_SIZE + XMAPOFFSET,
+                        j * SQUARE_SIZE + YMAPOFFSET,
+                        SQUARE_SIZE,
+                        SQUARE_SIZE);
+                    break;
+                case SQUARE_BLOCK:
+                    rb->lcd_bitmap_transparent(bomberman_block,
+                       i * SQUARE_SIZE + XMAPOFFSET,
+                       j * SQUARE_SIZE + YMAPOFFSET,
+                       BMPWIDTH_bomberman_block,
+                       BMPHEIGHT_bomberman_block);
+                    break;
+                case SQUARE_BOMB:
+                     /*
+                     rb->lcd_bitmap_transparent(bomberman_bomb,
+                                                i * SQUARE_SIZE + XMAPOFFSET,
+                                                j * SQUARE_SIZE + YMAPOFFSET,
+                                                BMPWIDTH_bomberman_bomb,
+                                                BMPHEIGHT_bomberman_bomb);
+                     */
+                    rb->lcd_bitmap_transparent_part(bomberman_bomb,
+                        game->field.det[i][j] * SQUARE_SIZE,
+                        0,
+                        STRIDE(SCREEN_MAIN,
+                               BMPWIDTH_bomberman_bomb,
+                               BMPHEIGHT_bomberman_bomb),
+                        i * SQUARE_SIZE + XMAPOFFSET,
+                        j * SQUARE_SIZE + YMAPOFFSET,
+                        SQUARE_SIZE,
+                        SQUARE_SIZE);
+                    break;
+                }
+
+        /* Player without movement. */
+        /*
+        rb->lcd_bitmap_transparent(bomberman_player,
+            game->players[i].xpos * SQUARE_SIZE + XMAPOFFSET,
+            game->players[i].ypos * SQUARE_SIZE + YMAPOFFSET -
+            (BMPHEIGHT_bomberman_player - SQUARE_SIZE),
+            BMPWIDTH_bomberman_player,
+            BMPHEIGHT_bomberman_player);
+        */
+
+	/* Player and ai's (with movement animation) */
+	for (i = 0; i < MAX_PLAYERS; i++)
+	{
+            if (game->draw_order[i]->status.state > GONNA_DIE)
+            {
+                if (game->draw_order[i]->status.state < DEAD)
+                {
+                    rb->lcd_bitmap_transparent_part(bomberman_player_death,
+                        (game->draw_order[i]->status.state - 2) * BMPWIDTH_bomberman_player,
+                        0,
+                        STRIDE(SCREEN_MAIN,
+                               BMPWIDTH_bomberman_player_death,
+                               BMPHEIGHT_bomberman_player_death),
+                        game->draw_order[i]->xpos * SQUARE_SIZE + XMAPOFFSET +
+                                        xcoord[game->draw_order[i]->rxpos + 1],
+                        game->draw_order[i]->ypos * SQUARE_SIZE + YMAPOFFSET -
+                        (BMPHEIGHT_bomberman_player - SQUARE_SIZE) -
+                                        ycoord[game->draw_order[i]->rypos + 1],
+                        BMPWIDTH_bomberman_player,
+                        BMPHEIGHT_bomberman_player);
+                }
+                else if (game->draw_order[i]->status.state > DEAD)
+                {
+                    rb->lcd_bitmap_transparent_part(win_bitmaps[game->draw_order[i]->num],
+                        (game->draw_order[i]->status.state % 2) * BMPWIDTH_bomberman_player,
+                        0,
+                        STRIDE(SCREEN_MAIN,
+                               BMPWIDTH_bomberman_player_win,
+                               BMPHEIGHT_bomberman_player_win),
+                        game->draw_order[i]->xpos * SQUARE_SIZE + XMAPOFFSET +
+                                        xcoord[game->draw_order[i]->rxpos + 1],
+                        game->draw_order[i]->ypos * SQUARE_SIZE + YMAPOFFSET -
+                                        (BMPHEIGHT_bomberman_player - SQUARE_SIZE) -
+                                        ycoord[game->draw_order[i]->rypos + 1],
+                        BMPWIDTH_bomberman_player,
+                        BMPHEIGHT_bomberman_player);
+                }
+            }
+            else
+            {
+                if (game->draw_order[i]->ismove)
+                {
+                    int curphase = move_cur_phases[game->draw_order[i]->move_phase];
+
+                    rb->lcd_bitmap_transparent_part(move_bitmaps[game->draw_order[i]->num],
+                        curphase * BMPWIDTH_bomberman_player,
+                        game->draw_order[i]->look * BMPHEIGHT_bomberman_player,
+                        STRIDE(SCREEN_MAIN, BMPWIDTH_bomberman_player_move, BMPHEIGHT_bomberman_player_move),
+                        game->draw_order[i]->xpos * SQUARE_SIZE + XMAPOFFSET +
+                            xcoord[game->draw_order[i]->rxpos + 1] +
+                            xcoord_k[game->draw_order[i]->look] * game->draw_order[i]->move_phase,
+                        game->draw_order[i]->ypos * SQUARE_SIZE + YMAPOFFSET -
+                            (BMPHEIGHT_bomberman_player - SQUARE_SIZE) -
+                            ycoord[game->draw_order[i]->rypos + 1] +
+                            ycoord_k[game->draw_order[i]->look] * game->draw_order[i]->move_phase,
+                        BMPWIDTH_bomberman_player,
+                        BMPHEIGHT_bomberman_player);
+                }
+                else
+                {
+                    rb->lcd_bitmap_transparent_part(move_bitmaps[game->draw_order[i]->num],
+                        0,
+                        game->draw_order[i]->look * BMPHEIGHT_bomberman_player,
+                        STRIDE(SCREEN_MAIN, BMPWIDTH_bomberman_player_move, BMPHEIGHT_bomberman_player_move),
+                        game->draw_order[i]->xpos * SQUARE_SIZE + XMAPOFFSET +
+                        xcoord[game->draw_order[i]->rxpos + 1],
+                        game->draw_order[i]->ypos * SQUARE_SIZE + YMAPOFFSET -
+                        (BMPHEIGHT_bomberman_player - SQUARE_SIZE) -
+                        ycoord[game->draw_order[i]->rypos + 1],
+                        BMPWIDTH_bomberman_player,
+                        BMPHEIGHT_bomberman_player);
+                }
+            }
+	}
+
+        /* Explosions :) */
+        for (i = 0; i < MAP_W; i++)
+            for (j = 0; j < MAP_H; j++)
+            {
+                int dir;
+
+                for (dir = 0; dir < 5; dir++)
+                {
+                    bool is_set = game->field.firemap[i][j] & (BITMASK_ALL_PHASES << dir_offsets[dir]);
+
+                    if (is_set)
+                    {
+                        bool is_end = game->field.firemap[i][j] & (BITMASK_IS_END << dir);
+                        int phase;
+
+                        for (phase = 0; phase < 4; phase++)
+                        {
+                            if (game->field.firemap[i][j] &
+                                    ((BITMASK_SING_PHASE << dir_offsets[dir]) << phase))
+                                break;
+                        }
+
+                        if (dir == FIRE_CENTER)
+                        {
+                            rb->lcd_bitmap_transparent_part(bomberman_explode,
+                                phase * SQUARE_SIZE,
+                                0,
+                                STRIDE(SCREEN_MAIN,
+                                       BMPWIDTH_bomberman_explode,
+                                       BMPHEIGHT_bomberman_explode),
+                                i * SQUARE_SIZE + XMAPOFFSET,
+                                j * SQUARE_SIZE + YMAPOFFSET,
+                                SQUARE_SIZE,
+                                SQUARE_SIZE);
+                        }
+                        else
+                        {
+                            rb->lcd_bitmap_transparent_part(bomberman_explode,
+                                dir * SQUARE_SIZE,
+                                SQUARE_SIZE + SQUARE_SIZE * phase * 2 +
+                                SQUARE_SIZE * is_end,
+                                STRIDE(SCREEN_MAIN,
+                                       BMPWIDTH_bomberman_explode,
+                                       BMPHEIGHT_bomberman_explode),
+                                i * SQUARE_SIZE + XMAPOFFSET,
+                                j * SQUARE_SIZE + YMAPOFFSET,
+                                SQUARE_SIZE,
+                                SQUARE_SIZE);
+                        }
+                    }
+                }
+            }
+    }
+    else if (game->state == GAME_GAMEOVER)
+    {
+        rb->lcd_bitmap(bomberman_gameover, 0, 0,
+                       BMPWIDTH_bomberman_gameover, BMPHEIGHT_bomberman_gameover);
+    }
+    else if (game->state == GAME_WON)
+    {
+        rb->lcd_bitmap(bomberman_youwon, 0, 0,
+                       BMPWIDTH_bomberman_youwon, BMPHEIGHT_bomberman_youwon);
+    }
+
+    /* Update the lcd. */
+    rb->lcd_update();
+}
diff --git a/apps/plugins/bomberman/draw.h b/apps/plugins/bomberman/draw.h
new file mode 100644
index 0000000..b86283b
--- /dev/null
+++ b/apps/plugins/bomberman/draw.h
@@ -0,0 +1,29 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2010-2011 Lev Panov, Nick Petrov
+ * 
+ * Bomberman plugin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _DRAW_H
+#define _DRAW_H
+
+void Draw(struct game_t *game);
+
+#endif /* _DRAW_H */
diff --git a/apps/plugins/bomberman/game.c b/apps/plugins/bomberman/game.c
new file mode 100644
index 0000000..36f473e
--- /dev/null
+++ b/apps/plugins/bomberman/game.c
@@ -0,0 +1,676 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2010-2011 Lev Panov, Nick Petrov
+ * 
+ * Bomberman plugin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#include "plugin.h"
+
+#include "game.h"
+
+enum fire_phase {
+    Phase1 = BOMB_EXPL_PHASE1,
+    Phase2 = BOMB_EXPL_PHASE2,
+    Phase3 = BOMB_EXPL_PHASE3,
+    Phase4 = BOMB_EXPL_PHASE4,
+    PhaseEnd = BOMB_NONE
+};
+
+struct fire_struct {
+     int x, y;
+     int rad;
+     enum fire_dir dir;
+     bool isFullPower;
+     enum fire_phase phase;
+     volatile uint32_t dir_bitmask;
+};
+
+/* Swap macro. */
+#define swap(a, b) { \
+    register typeof (a) tmp = a; \
+    a = b; \
+    b = tmp; \
+}
+
+void PlayerMoveUp(struct game_t *game, struct player_t *player)
+{
+    if (player->ismove || player->status.state != ALIVE)
+        return;
+
+    if (player->rxpos != 0)
+    {
+        if (player->rxpos == -1)
+            PlayerMoveRight(game, player);
+        else
+            PlayerMoveLeft(game, player);
+        return;
+    }
+
+    if (player->look != LOOK_UP)
+    {
+        player->look = LOOK_UP;
+        return;
+    }
+
+    if ((player->ypos > 0 && game->field.map[player->xpos][player->ypos - 1] == SQUARE_FREE)
+            || player->rypos == 1)
+    {
+        player->ismove = true;
+        player->move_phase = 1;
+        player->move_start_time = tick;
+    }
+}
+
+void PlayerMoveDown(struct game_t *game, struct player_t *player)
+{
+    if (player->ismove || player->status.state != ALIVE)
+        return;
+
+    if (player->rxpos != 0)
+    {
+        if (player->rxpos == -1)
+            PlayerMoveRight(game, player);
+        else
+            PlayerMoveLeft(game, player);
+        return;
+    }
+
+    if (player->look != LOOK_DOWN)
+    {
+        player->look = LOOK_DOWN;
+        return;
+    }
+
+    if ((player->ypos < MAP_H - 1 && game->field.map[player->xpos][player->ypos + 1] == SQUARE_FREE)
+            || player->rypos == -1)
+    {
+        player->ismove = true;
+        player->move_phase = 1;
+        player->move_start_time = tick;
+    }
+}
+
+void PlayerMoveRight(struct game_t *game, struct player_t *player)
+{
+    if (player->ismove || player->status.state != ALIVE)
+        return;
+
+    if (player->rypos != 0)
+    {
+        if (player->rypos == -1)
+            PlayerMoveDown(game, player);
+        else
+            PlayerMoveUp(game, player);
+        return;
+    }
+
+    if (player->look != LOOK_RIGHT)
+    {
+        player->look = LOOK_RIGHT;
+        return;
+    }
+
+    if ((player->xpos < MAP_W - 1 && game->field.map[player->xpos + 1][player->ypos] == SQUARE_FREE)
+            || player->rxpos == -1)
+    {
+        player->ismove = true;
+        player->move_phase = 1;
+        player->move_start_time = tick;
+    }
+}
+
+void PlayerMoveLeft(struct game_t *game, struct player_t *player)
+{
+    if (player->ismove || player->status.state != ALIVE)
+        return;
+
+    if (player->rypos != 0)
+    {
+        if (player->rypos == -1)
+            PlayerMoveDown(game, player);
+        else
+            PlayerMoveUp(game, player);
+        return;
+    }
+
+    if (player->look != LOOK_LEFT)
+    {
+        player->look = LOOK_LEFT;
+        return;
+    }
+
+    if ((player->xpos > 0 && game->field.map[player->xpos - 1][player->ypos] == SQUARE_FREE)
+            || player->rxpos == 1)
+    {
+        player->ismove = true;
+        player->move_phase = 1;
+        player->move_start_time = tick;
+    }
+}
+
+static void RecalcDrawOrder(struct game_t *game)
+{
+    int i, j, max;
+
+    max = MAX_PLAYERS - 1;
+    for (j = max; j > 0; j--)
+        for (i = 0; i < max; i++)
+        {
+            if (game->draw_order[i]->ypos > game->draw_order[i + 1]->ypos)
+            {
+                swap(game->draw_order[i], game->draw_order[i + 1]);
+            }
+            else if (game->draw_order[i]->ypos == game->draw_order[i + 1]->ypos)
+            {
+                if (game->draw_order[i]->rypos > game->draw_order[i + 1]->rypos)
+                {
+                    swap(game->draw_order[i], game->draw_order[i + 1]);
+                }
+            }
+        }
+}
+
+void PickBonus(struct game_t *game, struct player_t *player)
+{
+    int x = player->xpos, y = player->ypos;
+
+    switch (game->field.bonuses[x][y])
+    {
+    case BONUS_EXTRABOMB:
+        if (player->bombs_max > 3)
+            player->bombs_max = -1;
+        else if (player->bombs_max != -1)
+            player->bombs_max++;
+        break;
+    case BONUS_POWER:
+        if (player->power < BOMB_PWR_KILLER)
+            player->power++;
+        break;
+    case BONUS_SPEEDUP:
+        if (player->speed < 2)
+            player->speed++;
+        break;
+    case BONUS_FULLPOWER:
+        player->isFullPower = true;
+        break;
+    case BONUS_NONE:
+        break;
+    default:
+        break;
+    }
+
+    game->field.bonuses[x][y] = BONUS_NONE;
+}
+
+int UpdatePlayer(struct game_t *game, struct player_t *player)
+{
+    if (player->status.state == ALIVE)
+    {
+        if (player->ismove)
+        {
+            /* Control player speed. */
+            if (player->move_phase == game->max_move_phase[player->speed])
+            {
+                player->ismove = false;
+                player->move_phase = 0;
+
+                if (player->look == LOOK_UP)
+                {
+                    if (player->rypos == -1)
+                    {
+                        player->ypos--;
+                        player->rypos = 1;
+                    }
+                    else
+                        player->rypos--;
+                }
+                else if (player->look == LOOK_DOWN)
+                {
+                    if (player->rypos == 1)
+                    {
+                        player->ypos++;
+                        player->rypos = -1;
+                    }
+                    else
+                        player->rypos++;
+                }
+                else if (player->look == LOOK_RIGHT)
+                {
+                    if (player->rxpos == 1)
+                    {
+                        player->xpos++;
+                        player->rxpos = -1;
+                    }
+                    else
+                        player->rxpos++;
+                }
+                else /* LOOK_LEFT */
+                {
+                    if (player->rxpos == -1)
+                    {
+                        player->xpos--;
+                        player->rxpos = 1;
+                    }
+                    else
+                        player->rxpos--;
+                }
+
+                RecalcDrawOrder(game);
+                PickBonus(game, player);
+            }
+            else
+                player->move_phase++;
+        }
+    }
+    else if (player->status.state > GONNA_DIE && player->status.state < DEAD)
+    {
+        /* 2 -- ALIVE,GONNA_DIE */
+        player->status.state =
+                (tick - player->status.time_of_death) / PLAYER_DELAY_DEATH_ANIM + 2;
+
+        return player->status.state;
+    }
+    else if (player->status.state > DEAD)
+    {
+        static unsigned long won = 0;
+
+        if (!won)
+            won = tick;
+
+        if (tick - won > PLAYER_DELAY_WIN_ANIM_DUR)
+        {
+            if (player->isAI)
+                return -GAME_GAMEOVER;
+            else
+                return -GAME_WON;
+        }
+
+        player->status.state =
+                WIN_PHASE1 + (int)((tick - won) / PLAYER_DELAY_WIN_ANIM) % 2;
+    }
+
+    return 0;
+}
+
+void PlayerPlaceBomb(struct game_t *game, struct player_t *player)
+{
+    int i;
+
+    if (player->status.state != ALIVE)
+        return;
+
+    if (player->bombs_placed >= player->bombs_max &&
+        player->bombs_max != -1) /* Infinity. */
+            return;
+
+    for (i = 0; i < MAX_BOMBS; i++)
+        if (game->field.bombs[i].state > BOMB_NONE &&
+            game->field.bombs[i].xpos == player->xpos &&
+            game->field.bombs[i].ypos == player->ypos)
+                return;
+
+    for (i = 0; i < MAX_BOMBS; i++)
+        if (game->field.bombs[i].state == BOMB_NONE)
+        {
+            game->field.bombs[i].state = BOMB_PLACED;
+            game->field.bombs[i].xpos = player->xpos;
+            game->field.bombs[i].ypos = player->ypos;
+            game->field.bombs[i].power = player->power;
+            game->field.bombs[i].place_time = tick;
+            game->field.bombs[i].owner = player;
+            game->field.map[player->xpos][player->ypos] = SQUARE_BOMB;
+            game->field.det[player->xpos][player->ypos] = DET_PHASE1;
+            player->bombs_placed++;
+            break;
+        }
+}
+
+inline static bool IsTransparentSquare(struct field_t *field, int x, int y)
+{
+    return (field->map[x][y] == SQUARE_FREE ||
+           (field->map[x][y] == SQUARE_BOX && field->boxes[x][y].state > HUNKY));
+}
+
+static void DoFire(struct game_t *game, struct fire_struct *fs)
+{
+    int i, j;
+    int x = fs->x, y = fs->y, rad = fs->rad;
+    enum fire_dir dir = fs->dir;
+    bool isFullPower = fs->isFullPower;
+    enum fire_phase phase = fs->phase - 2;
+    volatile uint32_t dir_bitmask = fs->dir_bitmask;
+
+    /* Kill player in the center of explosion. */
+    if (phase == Phase1) {
+        for (i = 0; i < MAX_PLAYERS; i++)
+        {
+            if (game->players[i].xpos == x && game->players[i].ypos == y &&
+                    game->players[i].status.state == ALIVE)
+            {
+                game->players[i].status.state = GONNA_DIE;
+            }
+        }
+    }
+    else if (phase == PhaseEnd) {
+        for (i = 0; i < MAX_PLAYERS; i++)
+        {
+            if (game->players[i].xpos == x && game->players[i].ypos == y &&
+                    (game->players[i].status.state == ALIVE ||
+                     game->players[i].status.state == GONNA_DIE))
+            {
+                game->players[i].status.state = EXPL_PHASE1;
+                game->players[i].status.time_of_death = tick;
+            }
+        }
+    }
+
+    for (j = 1; j <= rad; j++)
+    {
+        int curx = 0, cury = 0;
+        int prevx = 0, prevy = 0;
+
+        switch (dir)
+        {
+        case FIRE_RIGNT:
+            curx = x + j;
+            cury = y;
+            prevx = curx - 1;
+            prevy = cury;
+            break;
+        case FIRE_DOWN:
+            curx = x;
+            cury = y + j;
+            prevx = curx;
+            prevy = cury - 1;
+            break;
+        case FIRE_LEFT:
+            curx = x - j;
+            cury = y;
+            prevx = curx + 1;
+            prevy = cury;
+            break;
+        case FIRE_UP:
+            curx = x;
+            cury = y - j;
+            prevx = curx;
+            prevy = cury + 1;
+            break;
+        default:
+            break;
+        }
+
+        bool already_used = (game->field.firemap[curx][cury] & (BITMASK_ALL_PHASES << (dir * 4)));
+        bool already_is_end = (game->field.firemap[curx][cury] & (BITMASK_IS_END << dir));
+
+        if ((dir == FIRE_RIGNT && curx < MAP_W - 1) ||
+            (dir == FIRE_DOWN  && cury < MAP_H - 1) ||
+            (dir == FIRE_LEFT  && curx >= 0) ||
+            (dir == FIRE_UP    && cury >= 0))
+        {
+            if (IsTransparentSquare(&game->field, curx, cury))
+            {
+                    game->field.firemap[curx][cury] |= (dir_bitmask << phase);
+                    if (j == rad) {
+                        if (!already_used)
+                            game->field.firemap[curx][cury] |= (BITMASK_IS_END << dir);
+                    }
+                    else {
+                        if (already_is_end)
+                            game->field.firemap[curx][cury] ^= (BITMASK_IS_END << dir);
+                    }
+            }
+            else if (game->field.map[curx][cury] == SQUARE_BOX)
+            {
+                game->field.firemap[curx][cury] |= (dir_bitmask << phase);
+                game->field.firemap[curx][cury] |= (BITMASK_IS_END << dir);
+
+                if (phase == PhaseEnd) {
+                    game->field.boxes[curx][cury].state = BOX_EXPL_PHASE1;
+                    game->field.boxes[curx][cury].expl_time = tick;
+                }
+
+                if (isFullPower) continue;
+                else             break;
+            }
+            else if (game->field.map[curx][cury] == SQUARE_BLOCK)
+            {
+                if (j > 1)
+                    game->field.firemap[prevx][prevy] |= (BITMASK_IS_END << dir);
+                break;
+            }
+
+            /* Detonate other bombs. */
+            else if (game->field.map[curx][cury] == SQUARE_BOMB)
+            {
+                for (i = 0; i < MAX_BOMBS; i++)
+                {
+                    if (game->field.bombs[i].xpos == curx &&
+                            game->field.bombs[i].ypos == cury &&
+                            game->field.bombs[i].state == BOMB_PLACED)
+                    {
+                        game->field.bombs[i].place_time = tick - BOMB_DELAY_DET;
+                        break;
+                    }
+                }
+            }
+
+            /* Player gets killed by explosion. */
+            if (phase == PhaseEnd) {
+                for (i = 0; i < MAX_PLAYERS; i++)
+                {
+                    if (game->players[i].xpos == curx && game->players[i].ypos == cury &&
+                            (game->players[i].status.state == ALIVE ||
+                             game->players[i].status.state == GONNA_DIE))
+                    {
+                        game->players[i].status.state = EXPL_PHASE1;
+                        game->players[i].status.time_of_death = tick;
+                    }
+                }
+            }
+            else {
+                for (i = 0; i < MAX_PLAYERS; i++)
+                {
+                    if (game->players[i].xpos == curx && game->players[i].ypos == cury &&
+                            game->players[i].status.state == ALIVE)
+                    {
+                        game->players[i].status.state = GONNA_DIE;
+                    }
+                }
+            }
+
+            if (game->field.bonuses[curx][cury] != BONUS_NONE) {
+                game->field.bonuses[curx][cury] = rb->rand() % (BONUS_NONE + 1);
+            }
+        }
+        else
+        {
+            if (j > 1)
+                game->field.firemap[prevx][prevy] |= (BITMASK_IS_END << dir);
+            break;
+        }
+    }
+}
+
+void UpdateBombs(struct game_t *game)
+{
+    int i;
+    /* Helps with detonation animation. */
+    static const int detphases[4] = { 0, 1, 2, 1 };
+    struct fire_struct fs;
+    int x, y, nticks;
+
+    /* Clear firemap. */
+    memset(game->field.firemap, 0*rb->rand() % 5, sizeof(game->field.firemap));
+
+    for (i = 0; i < MAX_BOMBS; i++)
+    {
+        if (game->field.bombs[i].state < BOMB_PLACED)
+            continue;
+
+        x = fs.x = game->field.bombs[i].xpos;
+        y = fs.y = game->field.bombs[i].ypos;
+        fs.rad = game->bomb_rad[game->field.bombs[i].power];
+        fs.isFullPower = game->field.bombs[i].owner->isFullPower;
+
+        nticks = tick - game->field.bombs[i].place_time;
+
+        /* Update detonation animation. */
+        game->field.det[x][y] = detphases[(nticks/* / BOMB_DELAY_DET_ANIM*/) % 4];
+
+        if (nticks >= BOMB_DELAY_PHASE4)
+        {
+            game->field.map[x][y] = SQUARE_FREE;
+            game->field.bombs[i].state = BOMB_NONE;
+
+            fs.phase = BOMB_NONE;
+
+            fs.dir = FIRE_RIGNT;
+            fs.dir_bitmask = BITMASK_RIGHT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_DOWN;
+            fs.dir_bitmask = BITMASK_DOWN;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_LEFT;
+            fs.dir_bitmask = BITMASK_LEFT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_UP;
+            fs.dir_bitmask = BITMASK_UP;
+            DoFire(game, &fs);
+
+            game->field.bombs[i].owner->bombs_placed--;
+        }
+        else if (nticks >= BOMB_DELAY_PHASE3)
+        {
+            game->field.map[x][y] = SQUARE_FREE;
+            game->field.bombs[i].state = BOMB_EXPL_PHASE4;
+            game->field.firemap[x][y] |= (BITMASK_CENTER << (BOMB_EXPL_PHASE4 - 2));
+
+            fs.phase = BOMB_EXPL_PHASE4;
+
+            fs.dir = FIRE_RIGNT;
+            fs.dir_bitmask = BITMASK_RIGHT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_DOWN;
+            fs.dir_bitmask = BITMASK_DOWN;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_LEFT;
+            fs.dir_bitmask = BITMASK_LEFT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_UP;
+            fs.dir_bitmask = BITMASK_UP;
+            DoFire(game, &fs);
+        }
+        else if (nticks >= BOMB_DELAY_PHASE2)
+        {
+            game->field.map[x][y] = SQUARE_FREE;
+            game->field.bombs[i].state = BOMB_EXPL_PHASE3;
+            game->field.firemap[x][y] |= (BITMASK_CENTER << (BOMB_EXPL_PHASE3 - 2));
+
+            fs.phase = BOMB_EXPL_PHASE3;
+
+            fs.dir = FIRE_RIGNT;
+            fs.dir_bitmask = BITMASK_RIGHT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_DOWN;
+            fs.dir_bitmask = BITMASK_DOWN;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_LEFT;
+            fs.dir_bitmask = BITMASK_LEFT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_UP;
+            fs.dir_bitmask = BITMASK_UP;
+            DoFire(game, &fs);
+        }
+        else if (nticks >= BOMB_DELAY_PHASE1)
+        {
+            game->field.map[x][y] = SQUARE_FREE;
+            game->field.bombs[i].state = BOMB_EXPL_PHASE2;
+            game->field.firemap[x][y] |= (BITMASK_CENTER << (BOMB_EXPL_PHASE2 - 2));
+
+            fs.phase = BOMB_EXPL_PHASE2;
+
+            fs.dir = FIRE_RIGNT;
+            fs.dir_bitmask = BITMASK_RIGHT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_DOWN;
+            fs.dir_bitmask = BITMASK_DOWN;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_LEFT;
+            fs.dir_bitmask = BITMASK_LEFT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_UP;
+            fs.dir_bitmask = BITMASK_UP;
+            DoFire(game, &fs);
+        }
+        else if (nticks >= BOMB_DELAY_DET)
+        {
+            game->field.map[x][y] = SQUARE_FREE;
+            game->field.bombs[i].state = BOMB_EXPL_PHASE1;
+            game->field.firemap[x][y] |= (BITMASK_CENTER << (BOMB_EXPL_PHASE1 - 2));
+
+            fs.phase = BOMB_EXPL_PHASE1;
+
+            fs.dir = FIRE_RIGNT;
+            fs.dir_bitmask = BITMASK_RIGHT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_DOWN;
+            fs.dir_bitmask = BITMASK_DOWN;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_LEFT;
+            fs.dir_bitmask = BITMASK_LEFT;
+            DoFire(game, &fs);
+
+            fs.dir = FIRE_UP;
+            fs.dir_bitmask = BITMASK_UP;
+            DoFire(game, &fs);
+        }
+    }
+}
+
+void UpdateBoxes(struct game_t *game)
+{
+    int i, j;
+    int nticks;
+
+    for (i = 0; i < MAP_W; i++)
+        for (j = 0; j < MAP_H; j++)
+            if (game->field.map[i][j] == SQUARE_BOX && game->field.boxes[i][j].state > HUNKY)
+            {
+                nticks = tick - game->field.boxes[i][j].expl_time;
+                game->field.boxes[i][j].state = nticks / BOX_DELAY_EXPLOSION_ANIM + 1;
+
+                if (game->field.boxes[i][j].state > BOX_EXPL_PHASE5)
+                    game->field.map[i][j] = SQUARE_FREE;
+            }
+}
diff --git a/apps/plugins/bomberman/game.h b/apps/plugins/bomberman/game.h
new file mode 100644
index 0000000..33f5b8d
--- /dev/null
+++ b/apps/plugins/bomberman/game.h
@@ -0,0 +1,258 @@
+/***************************************************************************
+ *             __________               __   ___.
+ *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
+ *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
+ *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
+ *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
+ *                     \/            \/     \/    \/            \/
+ * $Id$
+ *
+ * Copyright (C) 2010-2011 Lev Panov, Nick Petrov
+ * 
+ * Bomberman plugin
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+
+#ifndef _GAME_H
+#define _GAME_H
+
+/*
+ * Global game params.
+ */
+
+#define MAP_W 17
+#define MAP_H 11
+#define MAX_PLAYERS 4
+#define MAX_BOMBS 15
+
+/*
+ * Animation params.
+ */
+
+#define CYCLETIME 30
+
+#define BOMB_DELAY_DET    (HZ * 4    / (CYCLETIME / 10)) /* Delay before bomb detonates. */
+#define BOMB_DELAY_PHASE1 (HZ * 4.02 / (CYCLETIME / 10))
+#define BOMB_DELAY_PHASE2 (HZ * 4.03 / (CYCLETIME / 10))
+#define BOMB_DELAY_PHASE3 (HZ * 4.05 / (CYCLETIME / 10))
+#define BOMB_DELAY_PHASE4 (HZ * 4.06 / (CYCLETIME / 10))
+
+#define BOX_DELAY_EXPLOSION_ANIM (HZ * 0.04 / (CYCLETIME / 10))
+
+#define PLAYER_DELAY_DEATH_ANIM (HZ * 0.09 / (CYCLETIME / 10))
+#define PLAYER_DELAY_WIN_ANIM (HZ * 0.1 / (CYCLETIME / 10))
+#define PLAYER_DELAY_WIN_ANIM_DUR (HZ * 5 / (CYCLETIME / 10))
+
+#define AFTERGAME_DUR (HZ * 3 / (CYCLETIME / 10))
+
+/*
+ * Two types of ticks.
+ */
+
+#define get_tick() (*rb->current_tick)
+extern unsigned long tick;
+
+/*
+ * Enums and structs.
+ */
+
+enum square_type {
+	SQUARE_FREE = 0,
+	SQUARE_BOX,
+	SQUARE_BLOCK,
+	SQUARE_BOMB
+};
+
+enum look_side {
+	LOOK_UP = 0,
+	LOOK_DOWN,
+	LOOK_RIGHT,
+	LOOK_LEFT
+};
+
+enum bomb_power {
+	BOMB_PWR_SINGLE = 0,
+	BOMB_PWR_DOUBLE,
+	BOMB_PWR_TRIPLE,
+	BOMB_PWR_QUAD,
+	BOMB_PWR_KILLER
+};
+
+enum player_state {
+	ALIVE = 0,
+	GONNA_DIE,
+	EXPL_PHASE1,
+	EXPL_PHASE2,
+	EXPL_PHASE3,
+	EXPL_PHASE4,
+	DEAD,
+	WIN_PHASE1,
+	WIN_PHASE2
+};
+
+struct player_status {
+        enum player_state state;
+	unsigned long time_of_death;
+};
+
+struct player_t {
+        struct player_status status;
+	
+	int xpos, ypos;
+        enum look_side look;
+	int speed;
+	
+	int bombs_max;
+	int bombs_placed;
+        enum bomb_power power;
+        bool isFullPower;
+	
+	int rxpos, rypos;
+	bool ismove;
+	int move_phase;
+	unsigned long move_start_time;
+	
+        bool isAI;
+	
+	int num;
+};
+
+/*
+ * rxpos & rypos - position of player in single cell
+ * 
+ __________________
+ | -1  |  0  |  1  |
+ | -1  | -1  | -1  |
+ |_____|_____|_____|
+ | -1  |  0  |  1  |
+ |  0  |  0  |  0  |   <--- one cell
+ |_____|_____|_____|
+ | -1  |  0  |  1  |
+ |  1  |  1  |  1  |
+ |_____|_____|_____|
+ */
+
+/*
+ * Binary masks for fire map stuff.
+ */
+
+#define BITMASK_ALL_DIRS    0x000FFFFF
+#define BITMASK_ALL_PHASES  0x0000000F
+#define BITMASK_SING_PHASE  0x00000001
+
+#define BITMASK_RIGHT       0x00000001
+#define BITMASK_DOWN        0x00000010
+#define BITMASK_LEFT        0x00000100
+#define BITMASK_UP          0x00001000
+#define BITMASK_CENTER      0x00010000
+
+#define BITMASK_IS_END      0x00100000
+
+enum bomb_state {
+        BOMB_NONE = 0,
+        BOMB_PLACED,
+        BOMB_EXPL_PHASE1,
+        BOMB_EXPL_PHASE2,
+        BOMB_EXPL_PHASE3,
+        BOMB_EXPL_PHASE4
+};
+
+struct bomb_t {
+        enum bomb_state state;
+	int xpos, ypos;
+        enum bomb_power power;
+	unsigned long place_time;
+        struct player_t *owner;
+};
+
+enum fire_dir {
+	FIRE_RIGNT = 0,
+	FIRE_DOWN,
+	FIRE_LEFT,
+	FIRE_UP,
+	FIRE_CENTER
+};
+
+enum bomb_detonation {
+	DET_PHASE1 = 0,
+	DET_PHASE2,
+	DET_PHASE3
+};
+
+enum box_state {
+	HUNKY = 0,
+	BOX_EXPL_PHASE1,
+	BOX_EXPL_PHASE2,
+	BOX_EXPL_PHASE3,
+	BOX_EXPL_PHASE4,
+	BOX_EXPL_PHASE5
+};
+
+struct box_detonation {
+        enum box_state state;
+	unsigned long expl_time;
+};
+
+enum bonus_type {
+        BONUS_EXTRABOMB, /* Extra bomb. */
+        BONUS_POWER,     /* Increase bomb explosion radius. */
+        BONUS_SPEEDUP,   /* Increase player speed. */
+        BONUS_FULLPOWER, /* Destroy all destroyable objects in radius. */
+        BONUS_NONE       /* No bonus. */
+};
+
+struct field_t {
+        enum square_type map[MAP_W][MAP_H];
+        struct bomb_t bombs[MAX_BOMBS];
+        volatile uint32_t firemap[MAP_W][MAP_H];
+        enum bomb_detonation det[MAP_W][MAP_H];
+        struct box_detonation boxes[MAP_W][MAP_H];
+        enum bonus_type bonuses[MAP_W][MAP_H];
+};
+
+enum game_state {
+	GAME_GAME = 0,
+	GAME_GAMEOVER,
+	GAME_WON
+};
+
+struct game_t {
+        enum game_state state;
+        struct field_t field;
+        struct player_t players[MAX_PLAYERS];
+        struct player_t *draw_order[MAX_PLAYERS];
+	int nplayers;
+	int bomb_rad[5];
+        int max_move_phase[3];
+        int score;
+        int level;
+};
+
+/*
+ * Player control functions.
+ */
+
+void PlayerMoveUp(struct game_t *game, struct player_t *player);
+void PlayerMoveDown(struct game_t *game, struct player_t *player);
+void PlayerMoveRight(struct game_t *game, struct player_t *player);
+void PlayerMoveLeft(struct game_t *game, struct player_t *player);
+void PlayerPlaceBomb(struct game_t *game, struct player_t *player);
+
+/*
+ * Game update functions.
+ */
+
+int  UpdatePlayer(struct game_t *game, struct player_t *player);
+void UpdateBombs(struct game_t *game);
+void UpdateBoxes(struct game_t *game);
+void UpdateAftergame(struct game_t *game);
+
+#endif /* _GAME_H */
