diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..603f73e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+*Debug/
+Doc/
+Flow/Release/
+Flow/win8/
+Release/
diff --git a/Flow.sdf b/Flow.sdf
new file mode 100644
index 0000000..e4df47e
Binary files /dev/null and b/Flow.sdf differ
diff --git a/Flow.sln b/Flow.sln
new file mode 100644
index 0000000..411bd50
--- /dev/null
+++ b/Flow.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Flow", "Flow\Flow.vcxproj", "{A1EDC29D-DDCA-421C-92FE-7CADA9053585}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {A1EDC29D-DDCA-421C-92FE-7CADA9053585}.Debug|Win32.ActiveCfg = Debug|Win32
+ {A1EDC29D-DDCA-421C-92FE-7CADA9053585}.Debug|Win32.Build.0 = Debug|Win32
+ {A1EDC29D-DDCA-421C-92FE-7CADA9053585}.Release|Win32.ActiveCfg = Release|Win32
+ {A1EDC29D-DDCA-421C-92FE-7CADA9053585}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/Flow/DunkinSans.ttf b/Flow/DunkinSans.ttf
new file mode 100644
index 0000000..76ba3f4
Binary files /dev/null and b/Flow/DunkinSans.ttf differ
diff --git a/Flow/Flow.vcxproj b/Flow/Flow.vcxproj
new file mode 100644
index 0000000..7e3ab81
--- /dev/null
+++ b/Flow/Flow.vcxproj
@@ -0,0 +1,114 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {A1EDC29D-DDCA-421C-92FE-7CADA9053585}
+ Flow
+
+
+
+ Application
+ true
+ v120
+ MultiByte
+
+
+ Application
+ false
+ v120
+ true
+ Unicode
+
+
+
+
+
+
+
+
+
+
+
+
+ D:\Program Files\Microsoft Visual Studio 2012\SDL_ttf-2.0.11\include;D:\Program Files\Microsoft Visual Studio 2012\SDL-1.2.15\include;$(IncludePath)
+ D:\Program Files\Microsoft Visual Studio 2012\SDL_ttf-2.0.11\lib;D:\Program Files\Microsoft Visual Studio 2012\SDL-1.2.15\lib\x86;$(LibraryPath)
+
+
+ D:\Program Files\Microsoft Visual Studio 2012\SDL_ttf-2.0.11\include;D:\Program Files\Microsoft Visual Studio 2012\SDL-1.2.15\include;$(IncludePath)
+ D:\Program Files\Microsoft Visual Studio 2012\SDL_ttf-2.0.11\lib;D:\Program Files\Microsoft Visual Studio 2012\SDL-1.2.15\lib\x86;$(LibraryPath)
+ false
+
+
+
+ Level3
+ Disabled
+ CompileAsC
+ MultiThreadedDLL
+ false
+ true
+
+
+ true
+ SDL.lib;SDLmain.lib;SDL_gfx.lib;SDL_ttf.lib;SDL_image.lib;%(AdditionalDependencies)
+ Windows
+
+
+ true
+ true
+ true
+ true
+ UseLinkTimeCodeGeneration
+
+
+ true
+
+
+
+
+ Level3
+ CompileAsC
+ true
+ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)
+ true
+
+
+ SDL.lib;SDLmain.lib;SDL_gfx.lib;SDL_ttf.lib;SDL_image.lib;%(AdditionalDependencies)
+ Windows
+ true
+ true
+ true
+
+
+
+
+ false
+
+
+ true
+
+
+
+
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Flow/Flow.vcxproj.filters b/Flow/Flow.vcxproj.filters
new file mode 100644
index 0000000..baee300
--- /dev/null
+++ b/Flow/Flow.vcxproj.filters
@@ -0,0 +1,43 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+
+
+ Resource Files
+
+
+
+
+ Resource Files
+
+
+ Resource Files
+
+
+
+
+ Resource Files
+
+
+
+
+
+
\ No newline at end of file
diff --git "a/Flow/Sz\303\255nek.tif" "b/Flow/Sz\303\255nek.tif"
new file mode 100644
index 0000000..ad45f60
Binary files /dev/null and "b/Flow/Sz\303\255nek.tif" differ
diff --git a/Flow/cMark.png b/Flow/cMark.png
new file mode 100644
index 0000000..3909a11
Binary files /dev/null and b/Flow/cMark.png differ
diff --git a/Flow/defaultLevels.txt b/Flow/defaultLevels.txt
new file mode 100644
index 0000000..8e0323b
--- /dev/null
+++ b/Flow/defaultLevels.txt
@@ -0,0 +1,36 @@
+{{1,1,2,5},{5,1,4,4},{3,2,3,5},{3,1,2,4},{5,2,4,5},5,2,0}
+{{1,5,3,4},{1,1,5,4},{3,3,2,4},{1,4,5,5},5,2,0}
+{{4,2,3,3},{2,1,1,4},{4,1,4,5},{3,1,1,5},{4,4,3,5},5,2,0}
+{{4,1,1,2},{3,3,3,5},{5,1,1,5},{2,5,4,4},5,2,0}
+{{2,3,2,5},{5,1,3,5},{3,3,1,5},{1,1,3,4},5,2,0}
+{{4,2,2,2},{1,5,5,4},{1,1,5,3},{1,4,4,4},{1,2,4,3},5,2,0}
+{{4,2,2,2},{1,3,1,5},{2,4,4,5},{2,5,4,4},{1,2,5,2},5,2,0}
+{{2,2,5,1},{2,4,5,2},{3,1,5,5},{2,3,4,3},5,2,0}
+{{2,2,4,2},{1,5,5,4},{3,4,5,5},{1,4,5,3},5,2,0}
+{{2,1,5,1},{5,4,3,5},{1,1,5,3},{1,5,4,4},{1,4,4,3},5,2,0}
+{{1,1,5,1},{3,3,5,4},{5,3,2,5},{2,3,5,5},5,2,0}
+{{1,3,4,4},{5,4,4,5},{2,2,4,2},{1,2,2,4},5,2,0}
+{{2,1,1,3},{2,3,1,5},{2,5,5,5},{4,2,3,3},{2,2,3,4},5,2,0}
+{{1,5,3,5},{4,4,5,5},{2,2,4,1},{5,1,5,4},{3,2,3,4},5,1,0}
+{{1,1,3,4},{4,1,4,4},{3,1,3,3},{1,5,1,2},5,1,0}
+{{1,2,3,5},{1,5,4,4},{1,4,4,3},{2,2,4,2},5,2,0}
+{{1,5,3,4},{2,2,2,5},{3,5,5,4},{3,3,4,2},5,2,0}
+{{1,1,5,1},{5,2,4,5},{3,2,2,4},{2,2,4,2},5,2,0}
+{{1,5,6,3},{4,1,6,2},{3,2,5,5},{3,1,4,3},{2,2,3,4},6,2,0}
+{{4,1,5,3},{4,2,4,4},{6,1,6,3},{3,4,1,6},{2,2,6,4},{5,4,3,5},6,2,0}
+{{2,2,1,3},{3,2,2,5},{1,4,4,3},{1,2,6,4},{4,2,5,5},{6,5,4,6},6,2,0}
+{{2,1,2,6},{1,1,4,3},{2,5,5,5},{2,2,1,6},6,2,0}
+{{5,2,5,5},{1,2,3,6},{6,2,4,6},{5,3,5,6},{1,1,2,5},6,2,0}
+{{1,1,4,6},{3,5,2,6},{1,2,4,5},{1,6,4,4},{1,4,4,3},6,2,0}
+{{6,2,2,6},{4,4,5,5},{2,1,1,6},{4,3,6,3},{3,1,6,1},{3,3,5,4},6,2,0}
+{{3,1,4,5},{2,4,5,5},{1,1,3,2},{2,5,3,4},{1,2,4,2},{6,1,6,6},6,2,0}
+{{6,1,1,5},{1,2,4,4},{2,3,3,4},{1,1,4,5},{1,3,3,5},6,2,0}
+{{7,2,5,6},{5,5,6,6},{4,4,3,5},{7,1,6,7},{2,3,6,2},{5,4,7,7},7,2,0}
+{{3,1,4,7},{4,4,5,5},{4,2,6,2},{3,3,6,4},{3,4,6,5},{2,1,3,7},{3,2,3,6},7,2,0}
+{{3,4,7,5},{1,2,7,3},{1,5,7,7},{5,3,7,4},{4,3,6,5},{1,3,3,3},{1,1,7,2},7,1,0}
+{{1,6,6,6},{3,3,5,4},{1,2,1,7},{2,6,5,5},{3,5,5,6},7,2,0}
+{{2,3,3,5},{3,1,4,3},{6,2,5,5},{2,1,5,4},{2,4,7,7},{1,1,2,7},7,0,0}
+{{2,5,6,6},{1,2,3,5},{3,1,5,1},{6,1,7,4},{1,1,1,5},{6,2,5,4},{3,2,5,3},7,0,0}
+{{2,2,6,2},{2,5,6,5},{2,4,4,4},{1,2,2,3},{1,3,3,5},7,2,0}
+{{4,4,7,4},{3,4,3,6},{4,5,7,5},{5,1,6,7},{7,2,2,6},{2,2,7,1},{4,6,7,7},7,0,0}
+{{7,1,7,4},{2,4,6,6},{5,3,7,3},{3,2,6,5},{6,1,1,2},{2,2,7,5},7,0,0}
\ No newline at end of file
diff --git a/Flow/defaultLevelsBck.txt b/Flow/defaultLevelsBck.txt
new file mode 100644
index 0000000..1986129
--- /dev/null
+++ b/Flow/defaultLevelsBck.txt
@@ -0,0 +1,36 @@
+{{1,1,2,5},{5,1,4,4},{3,2,3,5},{3,1,2,4},{5,2,4,5},5,2,0}
+{{1,5,3,4},{1,1,5,4},{3,3,2,4},{1,4,5,5},5,2,0}
+{{4,2,3,3},{2,1,1,4},{4,1,4,5},{3,1,1,5},{4,4,3,5},5,2,0}
+{{4,1,1,2},{3,3,3,5},{5,1,1,5},{2,5,4,4},5,2,0}
+{{2,3,2,5},{5,1,3,5},{3,3,1,5},{1,1,3,4},5,2,0}
+{{4,2,2,2},{1,5,5,4},{1,1,5,3},{1,4,4,4},{1,2,4,3},5,2,0}
+{{4,2,2,2},{1,3,1,5},{2,4,4,5},{2,5,4,4},{1,2,5,2},5,2,0}
+{{2,2,5,1},{2,4,5,2},{3,1,5,5},{2,3,4,3},5,2,0}
+{{2,2,4,2},{1,5,5,4},{3,4,5,5},{1,4,5,3},5,2,0}
+{{2,1,5,1},{5,4,3,5},{1,1,5,3},{1,5,4,4},{1,4,4,3},5,2,0}
+{{1,1,5,1},{3,3,5,4},{5,3,2,5},{2,3,5,5},5,2,0}
+{{1,3,4,4},{5,4,4,5},{2,2,4,2},{1,2,2,4},5,2,0}
+{{2,1,1,3},{2,3,1,5},{2,5,5,5},{4,2,3,3},{2,2,3,4},5,2,0}
+{{1,5,3,5},{4,4,5,5},{2,2,4,1},{5,1,5,4},{3,2,3,4},5,2,0}
+{{1,1,3,4},{4,1,4,4},{3,1,3,3},{1,5,1,2},5,2,0}
+{{1,2,3,5},{1,5,4,4},{1,4,4,3},{2,2,4,2},5,2,0}
+{{1,5,3,4},{2,2,2,5},{3,5,5,4},{3,3,4,2},5,2,0}
+{{1,1,5,1},{5,2,4,5},{3,2,2,4},{2,2,4,2},5,2,0}
+{{1,5,6,3},{4,1,6,2},{3,2,5,5},{3,1,4,3},{2,2,3,4},6,2,0}
+{{4,1,5,3},{4,2,4,4},{6,1,6,3},{3,4,1,6},{2,2,6,4},{5,4,3,5},6,2,0}
+{{2,2,1,3},{3,2,2,5},{1,4,4,3},{1,2,6,4},{4,2,5,5},{6,5,4,6},6,2,0}
+{{2,1,2,6},{1,1,4,3},{2,5,5,5},{2,2,1,6},6,2,0}
+{{5,2,5,5},{1,2,3,6},{6,2,4,6},{5,3,5,6},{1,1,2,5},6,2,0}
+{{1,1,4,6},{3,5,2,6},{1,2,4,5},{1,6,4,4},{1,4,4,3},6,2,0}
+{{6,2,2,6},{4,4,5,5},{2,1,1,6},{4,3,6,3},{3,1,6,1},{3,3,5,4},6,2,0}
+{{3,1,4,5},{2,4,5,5},{1,1,3,2},{2,5,3,4},{1,2,4,2},{6,1,6,6},6,2,0}
+{{6,1,1,5},{1,2,4,4},{2,3,3,4},{1,1,4,5},{1,3,3,5},6,2,0}
+{{7,2,5,6},{5,5,6,6},{4,4,3,5},{7,1,6,7},{2,3,6,2},{5,4,7,7},7,2,0}
+{{3,1,4,7},{4,4,5,5},{4,2,6,2},{3,3,6,4},{3,4,6,5},{2,1,3,7},{3,2,3,6},7,2,0}
+{{3,4,7,5},{1,2,7,3},{1,5,7,7},{5,3,7,4},{4,3,6,5},{1,3,3,3},{1,1,7,2},7,2,0}
+{{1,6,6,6},{3,3,5,4},{1,2,1,7},{2,6,5,5},{3,5,5,6},7,2,0}
+{{2,3,3,5},{3,1,4,3},{6,2,5,5},{2,1,5,4},{2,4,7,7},{1,1,2,7},7,2,0}
+{{1,5,7,6},{3,5,5,2},{2,2,1,4},{7,1,6,2},{6,1,3,3},{6,4,7,6},{2,5,6,4},7,2,0}
+{{2,2,6,2},{2,5,6,5},{2,4,4,4},{1,2,2,3},{1,3,3,5},7,2,0}
+{{4,4,7,4},{3,4,3,6},{4,5,7,5},{5,1,6,7},{7,2,2,6},{2,2,7,1},{4,6,7,7},7,2,0}
+{{7,1,7,4},{2,4,6,6},{5,3,7,3},{3,2,6,5},{6,1,1,2},{2,2,7,5},7,2,0}
\ No newline at end of file
diff --git a/Flow/icon.bmp b/Flow/icon.bmp
new file mode 100644
index 0000000..e7cbe24
Binary files /dev/null and b/Flow/icon.bmp differ
diff --git a/Flow/left_arrow.png b/Flow/left_arrow.png
new file mode 100644
index 0000000..927f625
Binary files /dev/null and b/Flow/left_arrow.png differ
diff --git a/Flow/left_arrow_clicked.png b/Flow/left_arrow_clicked.png
new file mode 100644
index 0000000..3132602
Binary files /dev/null and b/Flow/left_arrow_clicked.png differ
diff --git a/Flow/main.New.c b/Flow/main.New.c
new file mode 100644
index 0000000..0c41b01
--- /dev/null
+++ b/Flow/main.New.c
@@ -0,0 +1,2008 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define FOR_EACH(element, first) \
+ for ((element) = (first); (element); (element) = (element)->next)
+
+typedef enum GameState
+{
+ MainMenu,
+ LevelSelectMenu,
+ TimeTrialMenu,
+ UserLevelLoading,
+ AboutMenu,
+ ActiveGame,
+ GameOver,
+ Exit
+} GameState;
+
+typedef enum FlowElementShape
+{
+ None = 0,
+ UpS = (1<<0),
+ RightS = (1<<1),
+ DownS = (1<<2),
+ LeftS = (1<<3),
+ EndS = (1<<4)
+} FlowElementShape;
+
+typedef enum FlowDirection
+{
+ FromFirst = 1<<0,
+ FromLast = 1<<1
+}FlowDirection;
+
+typedef struct FlowElement
+{
+ struct FlowElement *prev, *next;
+ SDL_Rect position;
+ FlowElementShape shape;
+} FlowElement;
+
+typedef struct Flow
+{
+ FlowElement *firstElement;
+ FlowElement *lastElement;
+ SDL_Color color;
+ int completed;
+ FlowDirection direction;
+} Flow;
+
+typedef enum LevelState
+{
+ Uncompleted, Completed, Starred
+} LevelState;
+
+typedef struct Level
+{
+ Flow *flows;
+ LevelState state;
+ unsigned int timeRecord;
+ int size, flowCount;
+} Level;
+
+typedef struct MenuItem
+{
+ char *name, mouseDown, mouseOver;
+ GameState gameState;
+ SDL_Color color;
+} MenuItem;
+
+typedef struct TimeTrialMenuItem
+{
+ char *name, mouseDown, mouseOver;
+ int index, time;
+ SDL_Color color;
+} TimeTrialMenuItem;
+
+typedef struct Button
+{
+ SDL_Rect position;
+ GameState gameState;
+ SDL_Surface *picture, *pictureDefault, *pictureMouseOver;
+} Button;
+
+typedef struct LevelTile
+{
+ SDL_Rect position;
+ char mouseDown;
+} LevelTile;
+
+typedef enum MouseButtonState
+{
+ Up, Down, JustUp, JustDown
+} MouseButtonState;
+
+const int
+ FONTSIZE_BIG = 130,
+ FONTSIZE_NORMAL = 50,
+ FONTSIZE_SMALL = 13,
+ MAINMENU_ITEM_MARGIN = 200,
+ LEVEL_TILE_MARGIN_TOP = 150,
+ LEVEL_TILE_PADDING = 5,
+ LEVEL_TILE_SIZE = 130,
+ GAME_AREA_SIZE = 400,
+ GAME_AREA_GRID_WIDTH = 3,
+ FLOW_SIZE_PERCENT = 27,
+ FLOW_END_SIZE_PERCENT = 54,
+ FLOW_BG_OPACITY = 7,
+ LEVEL_CHANGE_ARROW_DIST = 60,
+ TIME_TRIAL_MARGIN_LEFT = 50;
+const SDL_Color
+ LEVELTILE_COLOR = {96, 255, 47, 0},
+ GAME_AREA_GRID_COLOR = {0, 15, 0, 0},
+ FLOWCOLORS[] = {
+ 255, 0, 0, 0,
+ 255, 255, 0, 0, //yellow
+ 0, 255, 0, 0,
+ 0, 0, 255, 0,
+ 0, 255, 255, 0, //cyan
+ 255, 124, 0, 0, //orange
+ 140, 35, 163, 0,
+};
+SDL_TimerID userTimer;
+SDL_Surface *screen, *icon, *starPic, *cMarkPic;
+TTF_Font *fontTitle, *fontNormal, *fontSmall;
+GameState gameState;
+Level *currentLevels, *defaultLevels, *userLevels;
+int currentLevelIndex, currentLevelCount, defaultLevelCount, userLevelCount,
+ currentLevelSelectPage, levelSelectPageCount, innerFlowElementCount,
+ completedFlowCount ,currentTimeTTime, currentTimeTScore,
+ timeTHighScores[3], timeTScoreIndex;
+char exiting, screenBlurred, loadUserLevel, KeysDown[SDLK_LAST + 1] = {0},
+ *userLevelError = NULL, isTimeTrialGame;
+Uint32 currentTime;
+Uint32 aboutAnimation;
+MouseButtonState LMB;
+SDL_Rect mousePosition, mousePositionDown;
+MenuItem mainMenuItems[] = {
+ "arcade", 0, 0, LevelSelectMenu, {250,34,49},
+ "time trial", 0, 0, TimeTrialMenu, {255,191,31},
+ "user level", 0, 0, UserLevelLoading, {191,255,50},
+ "about", 0, 0, AboutMenu, {171,227,25},
+ "exit", 0, 0, Exit, {0, 196, 129}};
+Button arrowBack = {12, 33, 0, 0, MainMenu, NULL, NULL, NULL},
+ arrowNext = {0, 300, 0, 0, ActiveGame, NULL, NULL, NULL},
+ arrowPrev = {0, 300, 0, 0, LevelSelectMenu, NULL, NULL, NULL},
+ reload = {0, 300, 0, 0, ActiveGame, NULL, NULL, NULL},
+ menuButton = {0, 300, 0, 0, LevelSelectMenu, NULL, NULL, NULL};
+TimeTrialMenuItem timeTrialMenuItems[] = {
+ "30 sec", 0, 0, 0, 30, {250,34,49},
+ "60 sec", 0, 0, 1, 60, {255,191,31},
+ "90 sec", 0, 0, 2, 90, {191,255,50}};
+LevelTile levelTiles[9] = {0};
+FlowElement *flowElementStart;
+Flow *flowStart;
+
+///
+/// Gets the maximum of two int.
+///
+/// A number.
+/// A number.
+/// Returns the higher number
+///
+__inline int Max(int a, int b) {
+ return a > b ? a : b;
+}
+
+///
+/// Draws a string on a screen at a position with specified foreground and background color.
+///
+/// The surface.
+/// The target rect.
+/// The font.
+/// The string.
+/// The foreground color.
+/// The background color.
+///
+void DrawString(SDL_Surface *surface, SDL_Rect rect, TTF_Font *font, char *text, SDL_Color fg, SDL_Color bg)
+{
+ SDL_Surface *fontSurface = TTF_RenderText_Shaded(font, text, fg, bg);
+ if(fontSurface)
+ {
+ SDL_BlitSurface(fontSurface, 0, screen, &rect);
+ SDL_FreeSurface(fontSurface);
+ }
+}
+
+///
+/// Darkens the specified color.
+///
+/// The source color.
+/// The amount of change (0-1).
+/// Returns the changed color.
+///
+SDL_Color Darken(SDL_Color color, double amount)
+{
+ SDL_Color ret;
+ ret.r = (int) Max(0, color.r - 255 * amount);
+ ret.g = (int) Max(0, color.g - 255 * amount);
+ ret.b = (int) Max(0, color.b - 255 * amount);
+ return ret;
+}
+
+///
+/// Converts SDL_Color to UInt32.
+///
+/// The source color.
+/// Returns the converted color value.
+///
+Uint32 SDLColorTo32bit(SDL_Color color)
+{
+ Uint32 ret, t;
+ t = color.r;
+ ret = t << 24;
+ t = color.g;
+ ret |= t << 16;
+ t = color.b;
+ ret |= t << 8;
+ return ret |= 0xff;
+}
+
+///
+/// Sets the opacity of a color.
+///
+/// The source color.
+/// The opacity percent.
+/// Returns the changed color.
+///
+Uint32 SetOpacity(Uint32 color, int percent)
+{
+ return ((color>>8)<<8) | (Uint32)((double)0xff * (double)percent / 100);
+}
+
+///
+/// Gets a pixel from the surface.
+///
+/// The surface.
+/// The x coordinate.
+/// The y coordinate.
+/// Returns the pixel.
+///
+Uint32 GetPixel(SDL_Surface *surface, int x, int y)
+{
+ Uint8 bpp = surface->format->BytesPerPixel;
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
+ switch (bpp)
+ {
+ case 1:
+ return *p;
+ case 2:
+ return *(Uint16 *)p;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
+ return p[0] << 16 | p[1] << 8 | p[2];
+ else
+ return p[0] | p[1] << 8 | p[2] << 16;
+ case 4:
+ return *(Uint32 *)p;
+ default:
+ return 0;
+ }
+}
+
+///
+/// Sets a pixel on the surface.
+///
+/// The surface.
+/// The x coordinate.
+/// The y coordinate.
+/// The pixel.
+///
+void SetPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ int bpp = surface->format->BytesPerPixel;
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
+ switch (bpp)
+ {
+ case 1:
+ *p = pixel;
+ break;
+ case 2:
+ *(Uint16 *)p = pixel;
+ break;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
+ p[0] = (pixel >> 16) & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = pixel & 0xff;
+ }
+ else {
+ p[0] = pixel & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = (pixel >> 16) & 0xff;
+ }
+ break;
+ case 4:
+ *(Uint32 *)p = pixel;
+ break;
+ default:
+ break;
+ }
+}
+
+///
+/// Flips the surface horizontally.
+///
+/// The surface.
+/// Returns the flipped surface.
+///
+SDL_Surface *FlipH(SDL_Surface *surface)
+{
+ SDL_Surface *ret = NULL;
+ int x, y, rx, ry;
+ if(SDL_MUSTLOCK(surface))
+ SDL_LockSurface(surface);
+ ret = SDL_CreateRGBSurface(SDL_HWSURFACE, surface->w, surface->h,
+ surface->format->BitsPerPixel, surface->format->Rmask,
+ surface->format->Gmask, surface->format->Bmask, surface->format->Amask);
+ for(x = 0, rx = ret->w - 1; x < ret->w; x++, rx--)
+ for(y = 0, ry = ret->h - 1; y < ret->h; y++, ry--)
+ SetPixel(ret, rx, y, GetPixel(surface, x, y));
+ if(SDL_MUSTLOCK(surface))
+ SDL_UnlockSurface(surface);
+ return ret;
+}
+
+///
+/// Blurs the surface horizontally.
+///
+/// The surface.
+/// The blur amount.
+///
+void BlurH(SDL_Surface *surface, int amount)
+{
+ int x, y, hits, oldPixel, newPixel, bpp;
+ Uint32 color, *newColors, r, g, b;
+ bpp = surface->format->BytesPerPixel;
+ newColors = (Uint32 *)malloc(sizeof(Uint32 *) * surface->w);
+ for (y = 0; y < surface->h; y++)
+ {
+ hits = 0;
+ r = g = b = 0;
+ for (x = -amount; x < surface->w; x++)
+ {
+ oldPixel = x - amount - 1;
+ if (oldPixel >= 0)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + oldPixel * bpp);
+ if (color != 0)
+ {
+ r -= (color & 0xff0000) >> 16;
+ g -= (color & 0xff00) >> 8;
+ b -= (color & 0xff);
+ }
+ hits--;
+ }
+ newPixel = x + amount;
+ if (newPixel < surface->w)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + newPixel * bpp);
+ if (color != 0)
+ {
+ r += (color & 0xff0000) >> 16;
+ g += (color & 0xff00) >> 8;
+ b += (color & 0xff);
+ }
+ hits++;
+ }
+ if (x >= 0)
+ {
+ newColors[x] = (((int)((double)r / hits)) << 16)
+ | (((int)((double)g / hits)) << 8)
+ | (int)((double)b / hits);
+ }
+ }
+ for (x = 0; x < surface->w; x++)
+ *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + x * bpp) = newColors[x];
+ }
+ free(newColors);
+}
+
+///
+/// Blurs the surface vertically.
+///
+/// The surface.
+/// The blur amount.
+///
+void BlurW(SDL_Surface *surface, int amount)
+{
+ int x, y, hits, oldPixel, newPixel, bpp;
+ Uint32 color, *newColors, r, g, b;
+ bpp = surface->format->BytesPerPixel;
+ newColors = (Uint32 *)malloc(surface->h * sizeof(Uint32 *));
+ for (x = 0; x < surface->w; x++)
+ {
+ hits = 0;
+ r = g = b = 0;
+ for (y = -amount; y < surface->h; y++)
+ {
+ oldPixel = y - amount - 1;
+ if (oldPixel >= 0)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + oldPixel * surface->pitch + x * bpp);
+ if (color != 0)
+ {
+ r -= (color & 0xff0000) >> 16;
+ g -= (color & 0xff00) >> 8;
+ b -= (color & 0xff);
+ }
+ hits--;
+ }
+ newPixel = y + amount;
+ if (newPixel < surface->h)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + newPixel * surface->pitch + x * bpp);
+ if (color != 0)
+ {
+ r += (color & 0xff0000) >> 16;
+ g += (color & 0xff00) >> 8;
+ b += (color & 0xff);
+ }
+ hits++;
+ }
+ if (y >= 0)
+ {
+ newColors[y] = (((int)((double)r / hits)) << 16)
+ | (((int)((double)g / hits)) << 8)
+ | (int)((double)b / hits);
+ }
+ }
+ for (y = 0; y < surface->h; y++)
+ *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + x * bpp) = newColors[y];
+ }
+ free(newColors);
+}
+
+
+///
+/// Removes the flow element at the postition and removes the broken off FlowElements in a level.
+///
+/// The level.
+/// The x coordinate.
+/// The y coordinate.
+/// Determines whether to remove the FlowElement at the positon,
+/// or remove only the rest.
+/// Returns 1 if FlowElement can be removed or the position is free,
+/// 0 if the FlowElement at the position is the first or the last in a Flow.
+///
+int RemoveFlowElement(Level *level, int xIn, int yIn, char removeThis)
+{
+ Flow *f;
+ FlowElement *fElem1, *fElem2;
+ int a, b, i;
+ for (i = 0; i < level->flowCount; i++)
+ {
+ f = &level->flows[i];
+ FOR_EACH(fElem1, f->firstElement)
+ {
+ if (fElem1->position.x == xIn && fElem1->position.y == yIn)
+ {
+ if (f->completed)
+ {
+ if (fElem1 == f->firstElement || fElem1 == f->lastElement)
+ {
+ if (removeThis)
+ return 0;
+ else
+ {
+ RemoveFlowElement(level, f->firstElement->next->position.x, f->firstElement->next->position.y, 1);
+ RemoveFlowElement(level, f->lastElement->prev->position.x, f->lastElement->prev->position.y, 1);
+ f->direction = (FlowDirection)(FromFirst | FromLast);
+ f->completed = 0;
+ return 1;
+ }
+ }
+ //remove from xy until end (remove the shorter part)
+ for (fElem2 = fElem1->next, a = 0; fElem2 != f->lastElement; fElem2 = fElem2->next, a++);
+ for (fElem2 = fElem1->prev, b = 0; fElem2 != f->firstElement; fElem2 = fElem2->prev, b++);
+ if (a > b)
+ {
+ if (!removeThis) //skip one
+ fElem1 = fElem1->prev;
+ fElem1->next->prev = f->firstElement;
+ f->firstElement->next = fElem1->next;
+ f->direction = (FlowDirection)FromLast;
+ f->completed = 0;
+ RemoveFlowElement(level, f->lastElement->position.x, f->lastElement->position.y, 0);
+ }
+ else
+ {
+ if (!removeThis)
+ fElem1 = fElem1->next;
+ fElem1->prev->next = f->lastElement;
+ f->lastElement->prev = fElem1->prev;
+ f->direction = (FlowDirection)FromFirst;
+ f->completed = 0;
+ RemoveFlowElement(level, f->firstElement->position.x, f->firstElement->position.y, 1);
+ }
+ return 1;
+ }
+ else //not completed
+ {
+ if (removeThis)
+ {
+ if (fElem1 == f->firstElement || fElem1 == f->lastElement)
+ return 0;
+ if (f->direction & FromFirst)
+ {
+ fElem1->prev->next = f->lastElement;
+ f->lastElement->prev = fElem1->prev;
+ }
+ else
+ {
+ fElem1->next->prev = f->firstElement;
+ f->firstElement->next = fElem1->next;
+ }
+ do
+ {
+ fElem2 = (f->direction & FromFirst) ? fElem1->next : fElem1->prev;
+ free(fElem1);
+ fElem1 = fElem2;
+ }
+ while (!(fElem1 == f->lastElement || fElem1 == f->firstElement));
+ return 1;
+ }
+ else
+ {
+ if (f->direction & FromFirst)
+ {
+ if (fElem1->next != NULL)
+ return RemoveFlowElement(level, fElem1->next->position.x, fElem1->next->position.y, 1);
+ else
+ return 1;
+ }
+ else if (fElem1->prev != NULL)
+ return RemoveFlowElement(level, fElem1->prev->position.x, fElem1->prev->position.y, 1);
+ else
+ return 1;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+///
+/// Frees the level.
+///
+/// The level.
+///
+void FreeLevelContent(Level *level)
+{
+ int i;
+ if (level == NULL)
+ return;
+ for (i = level->flowCount - 1; i >= 0; i--)
+ {
+ RemoveFlowElement(level, level->flows[i].firstElement->next->position.x, level->flows[i].firstElement->next->position.y, 1);
+ RemoveFlowElement(level, level->flows[i].lastElement->prev->position.x, level->flows[i].lastElement->prev->position.y, 1);
+ free(level->flows[i].firstElement);
+ free(level->flows[i].lastElement);
+ level->flowCount--;
+ }
+}
+
+///
+/// Loads the levels from file.
+///
+/// The file.
+/// The levels.
+/// The level count.
+///
+void LoadLevelsFromFile(FILE *file, Level **levels, int *count)
+{
+ char c;
+ int i, flowColorIndex, countRet;
+ Flow *flow, *flowsTemp;
+ Level *levelRet = NULL, *levelTemp;
+ *levels = NULL;
+ *count = 0;
+
+ countRet = 1;
+ levelRet = (Level *)malloc(sizeof(Level));
+ for(c = fgetc(file); c != EOF && c != '{'; c = fgetc(file));
+ if (c == EOF)
+ return;
+ if((levelRet[countRet - 1].flows = (Flow *)malloc(sizeof(Flow))) == NULL)
+ return;
+ levelRet[countRet - 1].flowCount = 1;
+ flowColorIndex = 0;
+ while (1)
+ {
+ flow = &levelRet[countRet - 1].flows[levelRet[countRet - 1].flowCount - 1];
+ if((flow->firstElement = (FlowElement *)malloc(sizeof(FlowElement))) == NULL)
+ return;
+ if((flow->lastElement = (FlowElement *)malloc(sizeof(FlowElement))) == NULL)
+ return;
+ flow->firstElement->prev = NULL;
+ flow->firstElement->next = flow->lastElement;
+ flow->firstElement->shape = EndS;
+ flow->lastElement->next = NULL;
+ flow->lastElement->prev = flow->firstElement;
+ flow->lastElement->shape = EndS;
+ if(fscanf(file, "{%d,%d,%d,%d},",
+ &(flow->firstElement->position.x),
+ &(flow->firstElement->position.y),
+ &(flow->lastElement->position.x),
+ &(flow->lastElement->position.y)) < 4)
+ return;
+ flow->firstElement->position.x--;
+ flow->firstElement->position.y--;
+ flow->lastElement->position.x--;
+ flow->lastElement->position.y--;
+ flow->color = FLOWCOLORS[flowColorIndex++];
+ flow->completed = 0;
+ flow->direction = (FlowDirection)(FromFirst | FromLast);
+
+ if(fscanf(file, "%d,", &(levelRet[countRet - 1].size)) < 1) //there are more flows to read
+ {
+ if((flowsTemp = (Flow *)malloc(sizeof(Flow) * (levelRet[countRet - 1].flowCount + 1))) == NULL)
+ return;
+ for (i = 0; i < levelRet[countRet - 1].flowCount; i++)
+ flowsTemp[i] = levelRet[countRet - 1].flows[i];
+ free(levelRet[countRet - 1].flows);
+ levelRet[countRet - 1].flows = flowsTemp;
+ levelRet[countRet - 1].flowCount++;
+ }
+ else
+ {
+ if(fscanf(file, "%d,%d}",
+ &(levelRet[countRet - 1].state),
+ &(levelRet[countRet - 1].timeRecord)) < 2)
+ return;
+ for(c = fgetc(file); c != EOF && c != '{'; c = fgetc(file));
+ if (c == EOF)
+ {
+ *count = countRet;
+ *levels = levelRet;
+ return;
+ }
+ levelTemp = (Level *)malloc(sizeof(Level) * (countRet + 1));
+ for (i = 0; i < countRet; i++)
+ levelTemp[i] = levelRet[i];
+ free(levelRet);
+ levelRet = levelTemp;
+ countRet++;
+ if((levelRet[countRet - 1].flows = (Flow *)malloc(sizeof(Flow))) == NULL)
+ return;
+ levelRet[countRet - 1].flowCount = 1;
+ flowColorIndex = 0;
+ }
+ }
+}
+
+///
+/// Makes route from flowElemetStart to a position if it is possible
+///
+/// The x coordinate.
+/// The y coordinate.
+/// Returns -1 on error, 0 otherwise.
+///
+int MakeRoute(int x, int y)
+{
+ int i, j, k, l;
+ FlowElement *fe1;
+ if (flowElementStart == NULL || flowStart == NULL)
+ return -1;
+ if (flowStart->completed)
+ {
+ if (flowElementStart->position.x == x && flowElementStart->position.y == y)
+ RemoveFlowElement(¤tLevels[currentLevelIndex], flowElementStart->position.x, flowElementStart->position.y, 0);
+ else //completed flow, but not released mouse yet
+ {
+ //find flowelement if it is under xy
+ FOR_EACH(fe1, flowStart->firstElement)
+ {
+ if (fe1->position.x == x && fe1->position.y == y)
+ {
+ if (fe1 == flowStart->firstElement->next &&
+ (fe1 != flowStart->lastElement || flowElementStart == flowStart->lastElement))
+ {
+ flowStart->direction = (FlowDirection)FromLast;
+ flowStart->completed = 0;
+ }
+ else if(fe1 == flowStart->lastElement->prev &&
+ (fe1 != flowStart->firstElement || flowElementStart == flowStart->firstElement))
+ {
+ flowStart->direction = (FlowDirection)FromFirst;
+ flowStart->completed = 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+ else //not completed
+ {
+ if (flowElementStart == flowStart->firstElement)
+ flowStart->direction = (FlowDirection)FromFirst;
+ else if (flowElementStart == flowStart->lastElement)
+ flowStart->direction = (FlowDirection)FromLast;
+ //starts from the first
+ if (flowStart->direction & FromFirst)
+ {
+ //remove flowElements after xy
+ FOR_EACH(fe1, flowStart->firstElement)
+ {
+ if (fe1->position.x == x && fe1->position.y == y) //xy is in flowStart
+ RemoveFlowElement(¤tLevels[currentLevelIndex], fe1->position.x, fe1->position.y, 0);
+ }
+ //add new FLowElements (xy is not in FLowStart)
+ fe1 = flowStart->lastElement->prev;
+ k = y - fe1->position.y;
+ l = x - fe1->position.x;
+ //advenced route not yet implemented, only left and right connection
+ if (!(((k == 0) && (l != 0)) || ((l == 0) && (k != 0))))
+ return 0;
+ i = fe1->position.y + (k == 0 ? 0 : (k > 0 ? 1 : -1));
+ do
+ {
+ j = fe1->position.x + (l == 0 ? 0 : (l > 0 ? 1 : -1));
+ do
+ {
+ //flow completed
+ if (flowStart->lastElement->position.x == j && flowStart->lastElement->position.y == i)
+ {
+ fe1->next = flowStart->lastElement;
+ flowStart->lastElement->prev = fe1;
+ flowStart->completed = 1;
+ flowStart->direction = (FlowDirection)(FromFirst | FromLast);
+ return 0;
+ }
+ if(RemoveFlowElement(¤tLevels[currentLevelIndex], j, i, 1))
+ {
+ if((fe1->next = (FlowElement *)malloc(sizeof(FlowElement))) == NULL)
+ return -1;
+ flowStart->lastElement->prev = fe1->next;
+ fe1->next->next = flowStart->lastElement;
+ fe1->next->prev = fe1;
+ fe1->next->position.x = j;
+ fe1->next->position.y = i;
+ fe1 = flowStart->lastElement->prev;
+ k = y - fe1->position.y;
+ l = x - fe1->position.x;
+ if (!(((k == 0) && (l != 0)) || ((l == 0) && (k != 0))))
+ return 0;
+ }
+ else
+ return 0;
+ j += (l == 0 ? 0 : (l > 0 ? 1 : -1));
+ }while (j != x + l);
+ i += (k == 0 ? 0 : (k > 0 ? 1 : -1));
+ }while(i != y + k);
+ }
+ //starts from the last
+ else
+ {
+ //remove flowElements after xy
+ FOR_EACH(fe1, flowStart->firstElement)
+ {
+ if (fe1->position.x == x && fe1->position.y == y)//xy is in FlowStart
+ RemoveFlowElement(¤tLevels[currentLevelIndex], fe1->position.x, fe1->position.y, 0);
+ }
+ //add new FLowElements (xy is not in FLowStart)
+ fe1 = flowStart->firstElement->next;
+ l = x - fe1->position.x;
+ k = y - fe1->position.y;
+ if (!(((k == 0) && (l != 0)) || ((l == 0) && (k != 0))))
+ return 0;
+ i = fe1->position.y + (k == 0 ? 0 : (k > 0 ? 1 : -1));
+ do
+ {
+ j = fe1->position.x + (l == 0 ? 0 : (l > 0 ? 1 : -1));
+ do
+ {
+ //flow completed
+ if (flowStart->firstElement->position.x == j && flowStart->firstElement->position.y == i)
+ {
+ fe1->prev = flowStart->firstElement;
+ flowStart->firstElement->next = fe1;
+ flowStart->completed = 1;
+ flowStart->direction = (FlowDirection)(FromFirst | FromLast);
+ return 0;
+ }
+ if(RemoveFlowElement(¤tLevels[currentLevelIndex], j, i, 1))
+ {
+ fe1->prev = (FlowElement *)malloc(sizeof(FlowElement));
+ if (fe1->prev == NULL)
+ return -1;
+ flowStart->firstElement->next = fe1->prev;
+ fe1->prev->prev = flowStart->firstElement;
+ fe1->prev->next = fe1;
+ fe1->prev->position.x = j;
+ fe1->prev->position.y = i;
+ fe1 = flowStart->firstElement->next;
+ l = x - fe1->position.x;
+ k = y - fe1->position.y;
+ if (!(((k == 0) && (l != 0)) || ((l == 0) && (k != 0))))
+ return 0;
+ }
+ else
+ return 0;
+ j += l == 0 ? 0 : (l > 0 ? 1 : -1);
+ }while (j != x + l);
+ i += k == 0 ? 0 : (k > 0 ? 1 : -1);
+ }while(i != y + k);
+ }
+ }
+ return 0;
+}
+
+///
+/// Determines whether the button is clicked.
+///
+/// The button.
+/// Returns 1 if clicked, otherwise 0
+///
+int IsButtonClicked(Button *button)
+{
+ if (mousePosition.x >= button->position.x && mousePosition.x < button->position.x + button->picture->w &&
+ mousePosition.y >= button->position.y && mousePosition.y < button->position.y + button->picture->h)
+ if(LMB == JustUp)
+ return 1;
+ else
+ button->picture = button->pictureMouseOver;
+ else
+ button->picture = button->pictureDefault;
+ return 0;
+}
+
+///
+/// Determines whether a point is in the rectengle.
+///
+/// The x coordinate.
+/// The y coordinate.
+/// The width.
+/// The height.
+/// The point.
+/// Returns 1 if the point is in the rectangle, 0 otherwise.
+///
+int InRect(int x, int y, int w, int h, SDL_Rect point)
+{
+ return point.x >= x && point.x < x + w && point.y >= y && point.y < y + h;
+}
+
+///
+/// Adds connection to a flowElement by setting it's shape
+///
+/// The FlowElement to add connection from.
+/// The FlowElement to add connection.
+///
+void ShapeAddConnection(FlowElement *from, FlowElement *to)
+{
+ if (from->position.x > to->position.x)
+ {
+ to->shape = (FlowElementShape)(to->shape | RightS);
+ }
+ else if(from->position.x < to->position.x)
+ {
+ to->shape = (FlowElementShape)(to->shape | LeftS);
+ }
+ else if (from->position.y > to->position.y)
+ {
+ to->shape = (FlowElementShape)(to->shape | DownS);
+ }
+ else
+ {
+ to->shape = (FlowElementShape)(to->shape | UpS);
+ }
+}
+
+///
+/// Updates the FlowElement shapes
+///
+///
+void UpdateShapes()
+{
+ Level *level;
+ FlowElement *feA;
+ int i;
+ level = ¤tLevels[currentLevelIndex];
+ for (i = 0; i < level->flowCount; i++)
+ {
+ FOR_EACH(feA, level->flows[i].firstElement)
+ {
+ feA->shape = (feA == level->flows[i].firstElement || feA == level->flows[i].lastElement) ? EndS : None;
+ if (level->flows[i].completed)
+ {
+ if (feA->next != NULL)
+ {
+ ShapeAddConnection(feA->next, feA);
+ }
+ if (feA->prev != NULL)
+ {
+ ShapeAddConnection(feA->prev, feA);
+ }
+ }
+ else
+ {
+ if (feA->next != NULL &&
+ ((feA->next != level->flows[i].lastElement) || (level->flows[i].direction & FromLast) && (feA != level->flows[i].firstElement)) &&
+ ((feA != level->flows[i].firstElement) || (level->flows[i].direction & FromFirst) && (feA->next != level->flows[i].lastElement)))
+ {
+ ShapeAddConnection(feA->next, feA);
+ }
+ if (feA->prev != NULL &&
+ ((feA->prev != level->flows[i].firstElement) || (level->flows[i].direction & FromFirst) && (feA != level->flows[i].lastElement)) &&
+ ((feA != level->flows[i].lastElement) || (level->flows[i].direction & FromLast) && (feA->prev != level->flows[i].firstElement)))
+ {
+ ShapeAddConnection(feA->prev, feA);
+ }
+ }
+ }
+ }
+}
+
+///
+/// Sets the current level.
+///
+/// Index of the currentLevels.
+///
+void SetCurrentLevel(int levelIndex)
+{
+ int i;
+ if (currentLevelCount > 1)
+ {
+ if (levelIndex > currentLevelCount - 1)
+ levelIndex = currentLevelCount - 1;
+ else if (levelIndex < 0)
+ levelIndex = 0;
+ }
+ else
+ return;
+ currentLevelIndex = levelIndex;
+ innerFlowElementCount = 0;
+ completedFlowCount = 0;
+ //reset currentLevels
+ for (i = 0; i < currentLevels[levelIndex].flowCount; i++)
+ {
+ RemoveFlowElement(¤tLevels[levelIndex], currentLevels[levelIndex].flows[i].firstElement->position.x,
+ currentLevels[levelIndex].flows[i].firstElement->position.y, 0);
+ RemoveFlowElement(¤tLevels[levelIndex], currentLevels[levelIndex].flows[i].lastElement->position.x,
+ currentLevels[levelIndex].flows[i].lastElement->position.y, 0);
+ }
+ UpdateShapes();
+}
+
+///
+/// Processes the SDL events.
+///
+///
+void ProcessEvents()
+{
+ SDL_Event event;
+ if(SDL_WaitEvent(&event))
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ exiting = 1;
+ break;
+ case SDL_KEYDOWN:
+ KeysDown[event.key.keysym.sym] = 1;
+ break;
+ case SDL_KEYUP:
+ KeysDown[event.key.keysym.sym] = 0;
+ break;
+ case SDL_MOUSEMOTION:
+ mousePosition.x = event.motion.x;
+ mousePosition.y = event.motion.y;
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ if (event.button.button == 1)
+ {
+ mousePositionDown.x = mousePosition.x;
+ mousePositionDown.y = mousePosition.y;
+ LMB = JustDown;
+ }
+ break;
+ case SDL_MOUSEBUTTONUP:
+ if (event.button.button == 1)
+ LMB = JustUp;
+ break;
+ default:
+ break;
+ }
+}
+
+void Draw();
+
+///
+/// Updates the game logic.
+///
+/// Returns -1 on error, 0 otherwise.
+///
+int Update()
+{
+ int i, j, textW, textH, margin;
+ SDL_Rect v;
+ FlowElement *fElem1;
+ switch (gameState)
+ {
+ case MainMenu:
+ v.y = MAINMENU_ITEM_MARGIN;
+ for (i = 0; i < sizeof(mainMenuItems)/sizeof(MenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, mainMenuItems[i].name, &textW, &textH);
+ v.x = (screen->w - textW) / 2;
+ if (InRect(v.x, v.y, textW, textH, mousePosition))
+ {
+ mainMenuItems[i].mouseOver = 1;
+ if(LMB == JustDown)
+ mainMenuItems[i].mouseDown = 1;
+ else if(LMB == JustUp && mainMenuItems[i].mouseDown)
+ {
+ gameState = mainMenuItems[i].gameState;
+ mainMenuItems[i].mouseDown = 0;
+ Update();
+ break;
+ }
+ }
+ else
+ {
+ mainMenuItems[i].mouseOver = 0;
+ mainMenuItems[i].mouseDown = 0;
+ }
+ v.y += textH;
+ }
+ break;
+
+ case LevelSelectMenu:
+ currentLevels = defaultLevels;
+ currentLevelCount = defaultLevelCount;
+ arrowBack.gameState = MainMenu;
+ arrowNext.position.y = LEVEL_TILE_MARGIN_TOP + 3 * LEVEL_TILE_SIZE + 2 * LEVEL_TILE_PADDING + 20;
+ arrowPrev.position.y = arrowNext.position.y;
+ menuButton.gameState = LevelSelectMenu;
+ //Back button
+ if (IsButtonClicked(&arrowBack))
+ gameState = arrowBack.gameState;
+ if (currentLevelSelectPage > 0 && IsButtonClicked(&arrowPrev))
+ currentLevelSelectPage--;
+ if (currentLevelSelectPage < levelSelectPageCount - 1 && IsButtonClicked(&arrowNext))
+ currentLevelSelectPage++;
+ //Tiles
+ margin = (screen->w-(3 * LEVEL_TILE_SIZE + LEVEL_TILE_PADDING * 2)) / 2;
+ v.y = LEVEL_TILE_MARGIN_TOP;
+ for (i = 0; i < 3; i++)
+ {
+ v.x = margin;
+ for (j = 0; j < 3; j++)
+ {
+ if (InRect(v.x, v.y, LEVEL_TILE_SIZE, LEVEL_TILE_SIZE, mousePosition))
+ {
+ if(LMB == JustDown)
+ levelTiles[i * 3 + j].mouseDown = 1;
+ else
+ {
+ if(LMB == JustUp && levelTiles[i * 3 + j].mouseDown)
+ {
+ SetCurrentLevel(i * 3 + j + currentLevelSelectPage * 9);
+ gameState = ActiveGame;
+ Update();
+ arrowBack.gameState = LevelSelectMenu;
+ levelTiles[i * 3 + j].mouseDown = 0;
+ }
+ }
+ }
+ else
+ levelTiles[i * 3 + j].mouseDown = 0;
+ v.x += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ v.y += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ break;
+
+ case ActiveGame:
+ arrowNext.position.y = LEVEL_TILE_MARGIN_TOP + 3 * LEVEL_TILE_SIZE + 2 * LEVEL_TILE_PADDING + 20;
+ arrowPrev.position.y = arrowNext.position.y;
+ reload.position.y = arrowNext.position.y;
+ reload.position.x = screen->w / 2 - reload.picture->w / 2;
+ if (IsButtonClicked(&arrowBack))
+ gameState = arrowBack.gameState;
+ if (IsButtonClicked(&reload))
+ SetCurrentLevel(currentLevelIndex);
+ if (!isTimeTrialGame)
+ {
+ //next currentLevels button
+ if (currentLevelIndex != currentLevelCount - 1 && IsButtonClicked(&arrowNext))
+ {
+ SetCurrentLevel(currentLevelIndex + 1);
+ if (currentLevels == defaultLevels)
+ currentLevelSelectPage = (currentLevelIndex) / 9;
+ }
+ else if (currentLevelIndex > 0 && IsButtonClicked(&arrowPrev))
+ {
+ SetCurrentLevel(currentLevelIndex - 1);
+ if (currentLevels == defaultLevels)
+ currentLevelSelectPage = (currentLevelIndex) / 9;
+ }
+ }
+ else if (SDL_GetTicks() - 1000 > currentTime)
+ {
+ currentTime = SDL_GetTicks();
+ if (--currentTimeTTime == -1)
+ {
+ gameState = GameOver;
+ menuButton.gameState = TimeTrialMenu;
+ Update();
+ }
+ }
+ margin = (screen->w - GAME_AREA_SIZE) / 2;
+ if (InRect(margin, LEVEL_TILE_MARGIN_TOP, GAME_AREA_SIZE, GAME_AREA_SIZE, mousePosition))
+ {
+ //get clicked flow element
+ if(LMB == JustDown)
+ {
+ flowElementStart = NULL;
+ flowStart = NULL;
+ v.w = GAME_AREA_SIZE / currentLevels[currentLevelIndex].size; //flow element size
+ for (i = 0; i < currentLevels[currentLevelIndex].flowCount; i++)
+ {
+ FOR_EACH(fElem1, currentLevels[currentLevelIndex].flows[i].firstElement)
+ {
+ if(InRect(fElem1->position.x * v.w + margin,
+ fElem1->position.y * v.w + LEVEL_TILE_MARGIN_TOP, v.w, v.w, mousePosition))
+ {
+ flowStart = ¤tLevels[currentLevelIndex].flows[i];
+ flowElementStart = fElem1;
+ if (MakeRoute(flowElementStart->position.x, flowElementStart->position.y) == -1)
+ return -1; //memory error
+ UpdateShapes();
+ break;
+ }
+ }
+ }
+ }
+ else if (LMB == Down && flowElementStart != NULL && flowStart != NULL)
+ {
+ //convert mouse position to FlowElement posisiton
+ v.x = (double)(mousePosition.x - margin) / ((double)GAME_AREA_SIZE / currentLevels[currentLevelIndex].size);
+ v.y = (double)(mousePosition.y - LEVEL_TILE_MARGIN_TOP - 1) / ((double)GAME_AREA_SIZE / currentLevels[currentLevelIndex].size);
+ //connect FlowElements
+ if (MakeRoute(v.x, v.y) == -1)
+ return -1; //memory error
+ UpdateShapes();
+ //count completed Flows
+ completedFlowCount = 0;
+ innerFlowElementCount = 0;
+ for (i = 0; i < currentLevels[currentLevelIndex].flowCount; i++)
+ {
+ if (currentLevels[currentLevelIndex].flows[i].completed)
+ completedFlowCount++;
+ FOR_EACH(fElem1, currentLevels[currentLevelIndex].flows[i].firstElement)
+ innerFlowElementCount++;
+ innerFlowElementCount -= 2; //substract first and last
+ }
+ //check if game is over
+ if (currentLevels[currentLevelIndex].flowCount == completedFlowCount)
+ {
+ if (innerFlowElementCount + currentLevels[currentLevelIndex].flowCount * 2 ==
+ currentLevels[currentLevelIndex].size * currentLevels[currentLevelIndex].size)
+ currentLevels[currentLevelIndex].state = Starred;
+ else if (currentLevels[currentLevelIndex].state != Starred)
+ currentLevels[currentLevelIndex].state = Completed;
+ Draw(); //draw the last connection
+ if (isTimeTrialGame)
+ {
+ currentTimeTScore++;
+ SetCurrentLevel(rand() % defaultLevelCount);
+ }
+ else
+ {
+ gameState = GameOver;
+ Update();
+ screenBlurred = 0;
+ }
+ LMB = Up;
+ }
+ }
+ }
+ break;
+
+ case GameOver:
+ reload.position.x = screen->w / 2 - (int)(reload.picture->w * 0.5) - LEVEL_CHANGE_ARROW_DIST;
+ reload.position.y = 300;
+ arrowNext.position.y = 300;
+ arrowNext.gameState = ActiveGame;
+ if (IsButtonClicked(&menuButton))
+ gameState = menuButton.gameState;
+ if (!isTimeTrialGame)
+ {
+ //reload currentLevels button
+ if (currentLevels[currentLevelIndex].state != Starred && IsButtonClicked(&reload))
+ {
+ gameState = reload.gameState;
+ reload.picture = reload.pictureDefault;
+ }
+ //next currentLevels button
+ if (currentLevelIndex != currentLevelCount - 1 && IsButtonClicked(&arrowNext))
+ {
+ SetCurrentLevel(currentLevelIndex + 1);
+ currentLevelSelectPage = (currentLevelIndex + 1) / 10;
+ gameState = arrowNext.gameState;
+ Update();
+ }
+ }
+ else if (timeTHighScores[timeTScoreIndex] < currentTimeTScore)
+ timeTHighScores[timeTScoreIndex] = currentTimeTScore;
+ break;
+
+ case TimeTrialMenu:
+ currentLevels = defaultLevels;
+ arrowBack.gameState = MainMenu;
+ isTimeTrialGame = 0;
+ if (IsButtonClicked(&arrowBack))
+ gameState = arrowBack.gameState;
+ v.y = MAINMENU_ITEM_MARGIN;
+ v.x = TIME_TRIAL_MARGIN_LEFT;
+ for (i = 0; i < sizeof(timeTrialMenuItems)/sizeof(TimeTrialMenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, timeTrialMenuItems[i].name, &textW, &textH);
+ if (InRect(v.x, v.y, textW, textH, mousePosition))
+ {
+ timeTrialMenuItems[i].mouseOver = 1;
+ if(LMB == JustDown)
+ timeTrialMenuItems[i].mouseDown = 1;
+ else if(LMB == JustUp && timeTrialMenuItems[i].mouseDown)
+ {
+ gameState = ActiveGame;
+ timeTrialMenuItems[i].mouseDown = 0;
+ timeTrialMenuItems[i].mouseOver = 0;
+ currentTimeTTime = timeTrialMenuItems[i].time + 1;
+ timeTScoreIndex = timeTrialMenuItems[i].index;
+ isTimeTrialGame = 1;
+ currentTimeTScore = 0;
+ SetCurrentLevel(rand() % defaultLevelCount);
+ arrowBack.gameState = TimeTrialMenu;
+ Update();
+ }
+ }
+ else
+ {
+ timeTrialMenuItems[i].mouseDown = 0;
+ timeTrialMenuItems[i].mouseOver = 0;
+ }
+ v.y += textH;
+ }
+ break;
+
+ case UserLevelLoading:
+ if (loadUserLevel)
+ {
+ FILE *file;
+ loadUserLevel = 0;
+ menuButton.gameState = MainMenu;
+ if ((file = fopen("userLevels.txt", "rt")) == NULL)
+ {
+ userLevelError = "\"userLevels.txt\" could not be loaded.";
+ userLevels = NULL;
+ userLevelCount = 0;
+ }
+ else
+ {
+ if (userLevels) //free previously loaded currentLevels
+ {
+ for (i = 0; i < userLevelCount; i++)
+ FreeLevelContent(&userLevels[i]);
+ free(userLevels);
+ }
+ LoadLevelsFromFile(file, &userLevels, &userLevelCount);
+ if (!userLevels)
+ userLevelError = "\"userLevels.txt\" contains wrong format.";
+ else
+ {
+ currentLevels = userLevels;
+ currentLevelCount = userLevelCount;
+ SetCurrentLevel(0);
+ loadUserLevel = 1;
+ gameState = ActiveGame;
+ Update();
+ }
+ fclose(file);
+ }
+ screenBlurred = 0;
+ }
+ if (IsButtonClicked(&menuButton))
+ {
+ gameState = menuButton.gameState;
+ loadUserLevel = 1;
+ }
+ break;
+
+ case AboutMenu:
+ if (IsButtonClicked(&arrowBack))
+ gameState = arrowBack.gameState;
+ break;
+
+ case Exit:
+ exiting = 1;
+ break;
+ }
+ switch (LMB)
+ {
+ case JustUp:
+ LMB = Up;
+ break;
+ case JustDown:
+ LMB = Down;
+ break;
+ default:
+ break;
+ }
+ if (KeysDown[SDLK_ESCAPE])
+ exiting = 1;
+ return 0;
+}
+
+///
+/// Draws the game.
+///
+///
+void Draw()
+{
+ SDL_Color white = {255,255,255};
+ SDL_Color black = {0,0,0};
+ char str[60];
+ int i, j, k, textW, textH, margin;
+ SDL_Rect r = {0, 0, 0, 0};
+ Flow *f;
+ FlowElement *fElem1;
+ switch (gameState)
+ {
+ case MainMenu:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ TTF_SizeText(fontTitle,"flow", &textW, NULL);
+ r.x = (screen->w - textW) / 2;
+ DrawString(screen, r, fontTitle, "flow", white, black);
+ r.y = MAINMENU_ITEM_MARGIN;
+ for (i = 0; i < sizeof(mainMenuItems)/sizeof(MenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, mainMenuItems[i].name, &textW, &textH);
+ r.x = (screen->w - textW) / 2;
+ if (mainMenuItems[i].mouseOver)
+ {
+ if (mainMenuItems[i].mouseDown)
+ DrawString(screen, r, fontNormal, mainMenuItems[i].name, Darken(mainMenuItems[i].color, 0.3), mainMenuItems[i].color);
+ else
+ DrawString(screen, r, fontNormal, mainMenuItems[i].name, Darken(mainMenuItems[i].color, 0.3), black);
+ }
+ else
+ DrawString(screen, r, fontNormal, mainMenuItems[i].name, mainMenuItems[i].color, black);
+ r.y += textH;
+ }
+ break;
+
+ case LevelSelectMenu:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ if (currentLevelSelectPage > 0)
+ {
+ r.x = arrowPrev.position.x;
+ r.y = arrowPrev.position.y;
+ SDL_BlitSurface(arrowPrev.picture, 0, screen, &r);
+ }
+ if (currentLevelSelectPage < levelSelectPageCount - 1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+
+ //current/all LevelTile page
+ *str = 0;
+ sprintf(str, "%d/%d", currentLevelSelectPage + 1, levelSelectPageCount);
+ TTF_SizeText(fontSmall, str, &textW, NULL);
+ r.x = screen->w / 2 - textW / 2;
+ r.y = LEVEL_TILE_MARGIN_TOP - 30;
+ DrawString(screen, r, fontSmall, str, white, black);
+
+ //Tiles
+ margin = (screen->w - (3 * LEVEL_TILE_SIZE + LEVEL_TILE_PADDING * 2)) / 2;
+ r.y = LEVEL_TILE_MARGIN_TOP;
+ for (i = 0; i < 3; i++)
+ {
+ r.x = margin;
+ for (j = 0; j < 3; j++)
+ {
+ sprintf(str, "%d", i * 3 + j + 1 + currentLevelSelectPage * 9);
+ TTF_SizeText(fontNormal, str, &textW, &textH);
+ if (levelTiles[i * 3 + j].mouseDown)
+ {
+ boxColor(screen, r.x, r.y, r.x + LEVEL_TILE_SIZE, r.y + LEVEL_TILE_SIZE,
+ SDLColorTo32bit(Darken(LEVELTILE_COLOR, 0.3)));
+ r.x += (LEVEL_TILE_SIZE - textW) / 2;
+ r.y += (LEVEL_TILE_SIZE - textH) / 2;
+ DrawString(screen, r, fontNormal, str, black, Darken(LEVELTILE_COLOR, 0.3));
+ r.x -= (LEVEL_TILE_SIZE - textW) / 2;
+ r.y -= (LEVEL_TILE_SIZE - textH) / 2;
+ }
+ else
+ {
+ boxColor(screen, r.x, r.y, r.x + LEVEL_TILE_SIZE, r.y + LEVEL_TILE_SIZE,
+ SDLColorTo32bit(LEVELTILE_COLOR));
+ r.x += (LEVEL_TILE_SIZE - textW) / 2;
+ r.y += (LEVEL_TILE_SIZE - textH) / 2;
+ DrawString(screen, r, fontNormal, str, black, LEVELTILE_COLOR);
+ r.x -= (LEVEL_TILE_SIZE - textW) / 2;
+ r.y -= (LEVEL_TILE_SIZE - textH) / 2;
+ }
+ //level complete indicator
+ if (i * 3 + j + currentLevelSelectPage * 9 < currentLevelCount)
+ {
+ r.x += LEVEL_TILE_SIZE - cMarkPic->w - 3;
+ r.y += LEVEL_TILE_SIZE - cMarkPic->h - 3;
+ switch (currentLevels[i * 3 + j + currentLevelSelectPage * 9].state)
+ {
+ case Completed:
+ SDL_BlitSurface(cMarkPic, 0, screen, &r);
+ break;
+ case Starred:
+ SDL_BlitSurface(starPic, 0, screen, &r);
+ break;
+ default:
+ break;
+ }
+ r.y -= LEVEL_TILE_SIZE - cMarkPic->h - 3;
+ r.x -= LEVEL_TILE_SIZE - cMarkPic->w - 3;
+ }
+ r.x += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ r.y += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ r.x = 50;
+ r.y = 0;
+ DrawString(screen, r, fontNormal, "choose levels", white, black);
+ break;
+
+ case ActiveGame:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ //back button
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ if (!isTimeTrialGame)
+ {
+ //previous currentLevels button
+ if (currentLevelIndex > 0)
+ {
+ r.x = arrowPrev.position.x;
+ r.y = arrowPrev.position.y;
+ SDL_BlitSurface(arrowPrev.picture, 0, screen, &r);
+ }
+ //next currentLevels button
+ if (currentLevelIndex != currentLevelCount - 1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+ }
+ else
+ {
+ //time left
+ *str = 0;
+ sprintf(str, "%d", currentTimeTTime);
+ TTF_SizeText(fontSmall, str, &textW, &textH);
+ r.x = (screen->w + GAME_AREA_SIZE) / 2 - 20;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ DrawString(screen, r, fontSmall, str, white, black);
+ //current score
+ *str = 0;
+ sprintf(str, "completed: %d", currentTimeTScore);
+ TTF_SizeText(fontSmall, str, &textW, &textH);
+ r.x = (screen->w + textW) / 2;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ DrawString(screen, r, fontSmall, str, white, black);
+ }
+ //reload currentLevels button
+ r.x = reload.position.x;
+ r.y = reload.position.y;
+ SDL_BlitSurface(reload.picture, 0, screen, &r);
+ margin = (screen->w - GAME_AREA_SIZE)/2;
+ //comleted/all flow count
+ *str = 0;
+ sprintf(str, "flows: %d/%d", completedFlowCount, currentLevels[currentLevelIndex].flowCount);
+ TTF_SizeText(fontSmall, str, &textW, &textH);
+ r.x = margin;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ DrawString(screen, r, fontSmall, str, white, black);
+ //percent
+ *str = 0;
+ sprintf(str, "%d%%",
+ (int)((double)(innerFlowElementCount + completedFlowCount) * 100.0 /
+ (double)(currentLevels[currentLevelIndex].size * currentLevels[currentLevelIndex].size - currentLevels[currentLevelIndex].flowCount)));
+ r.x = margin + 100;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ DrawString(screen, r, fontSmall, str, white, black);
+ //currentLevels state
+ r.x = screen->w - cMarkPic->w - 10;
+ r.y = 30;
+ switch (currentLevels[currentLevelIndex].state)
+ {
+ case Completed:
+ SDL_BlitSurface(cMarkPic, 0, screen, &r);
+ break;
+ case Starred:
+ SDL_BlitSurface(starPic, 0, screen, &r);
+ break;
+ default:
+ break;
+ }
+ //horizontal grid
+ for (i = 0; i <= currentLevels[currentLevelIndex].size; i++)
+ {
+ r.x = margin - GAME_AREA_GRID_WIDTH;
+ r.w = GAME_AREA_GRID_WIDTH + currentLevels[currentLevelIndex].size *
+ ((GAME_AREA_SIZE - (currentLevels[currentLevelIndex].size - 1) *
+ GAME_AREA_GRID_WIDTH) / currentLevels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH) - 1;
+ r.y = LEVEL_TILE_MARGIN_TOP - GAME_AREA_GRID_WIDTH +
+ i * ((GAME_AREA_SIZE - (currentLevels[currentLevelIndex].size - 1) *
+ GAME_AREA_GRID_WIDTH) / currentLevels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH);
+ r.h = GAME_AREA_GRID_WIDTH - 1;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SDLColorTo32bit(GAME_AREA_GRID_COLOR));
+ }
+ //vertical grid
+ for (i = 0; i <= currentLevels[currentLevelIndex].size; i++)
+ {
+ r.x = margin - GAME_AREA_GRID_WIDTH +
+ i * ((GAME_AREA_SIZE - (currentLevels[currentLevelIndex].size - 1) *
+ GAME_AREA_GRID_WIDTH) / currentLevels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH);
+ r.w = GAME_AREA_GRID_WIDTH - 1;
+ r.y = LEVEL_TILE_MARGIN_TOP - GAME_AREA_GRID_WIDTH;
+ r.h = GAME_AREA_SIZE + GAME_AREA_GRID_WIDTH - 1;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SDLColorTo32bit(GAME_AREA_GRID_COLOR));
+ }
+ //Flows
+ r.w = (GAME_AREA_SIZE - (currentLevels[currentLevelIndex].size - 1) * GAME_AREA_GRID_WIDTH) / currentLevels[currentLevelIndex].size - 1;
+ r.h = r.w;
+ i = r.w * FLOW_SIZE_PERCENT / 100.0; //current flow width
+ j = r.w * FLOW_END_SIZE_PERCENT / 100.0; //current flow end width
+ for (k = 0; k < currentLevels[currentLevelIndex].flowCount; k++)
+ {
+ f = ¤tLevels[currentLevelIndex].flows[k];
+ FOR_EACH(fElem1, currentLevels[currentLevelIndex].flows[k].firstElement)
+ {
+ r.x = fElem1->position.x * (r.w + GAME_AREA_GRID_WIDTH + 1) + margin;
+ r.y = fElem1->position.y * (r.w + GAME_AREA_GRID_WIDTH + 1) + LEVEL_TILE_MARGIN_TOP;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SetOpacity(SDLColorTo32bit(f->color), FLOW_BG_OPACITY));
+ if (fElem1->shape & UpS)
+ {
+ boxColor(screen, r.x+(r.w - i) / 2, r.y, r.x + (r.w - i) / 2 + i,
+ r.y +(r.h - i) / 2 + i, SDLColorTo32bit(f->color));
+ }
+ //overlap grid
+ if (fElem1->shape & DownS)
+ {
+ boxColor(screen, r.x + (r.w - i) / 2, r.y + r.w + GAME_AREA_GRID_WIDTH,
+ r.x+(r.w - i) / 2 + i, r.y + (r.h - i) / 2, SDLColorTo32bit(f->color));
+ }
+ //overlap grid
+ if (fElem1->shape & RightS)
+ {
+ boxColor(screen, r.x + (r.w - i) / 2, r.y +(r.h - i) / 2 + i, r.x + r.w +
+ GAME_AREA_GRID_WIDTH, r.y + (r.h - i) / 2, SDLColorTo32bit(f->color));
+ }
+ if (fElem1->shape & LeftS)
+ {
+ boxColor(screen, r.x, r.y +(r.h - i)/2, r.x+(r.w - i) / 2 + i,
+ r.y +(r.h - i) / 2 + i, SDLColorTo32bit(f->color));
+ }
+ if (fElem1->shape & EndS)
+ {
+ boxColor(screen, r.x + (r.w - j) / 2, r.y +(r.h - j) / 2, r.x+
+ (r.w - j) / 2 + j, r.y +(r.h - j) / 2 +
+ j, SDLColorTo32bit(f->color));
+ }
+ }
+ }
+ //print level name
+ *str = 0;
+ sprintf(str, "level %d", currentLevelIndex + 1);
+ r.x = 50;
+ r.y = 0;
+ DrawString(screen, r, fontNormal, str, white, black);
+ break;
+
+ case GameOver:
+ //blur previous screen
+ if (!screenBlurred)
+ {
+ BlurH(screen, 2);
+ BlurW(screen, 2);
+ screenBlurred = 1;
+ }
+ r.x = menuButton.position.x;
+ r.y = menuButton.position.y;
+ SDL_BlitSurface(menuButton.picture, 0, screen, &r);
+ if (!isTimeTrialGame)
+ {
+ if (currentLevels[currentLevelIndex].state != Starred)
+ {
+ TTF_SizeText(fontSmall, "fill the whole game area.", &textW, &textH);
+ r.y = arrowNext.position.y - arrowNext.picture->h - textH;
+ r.x = screen->w / 2 - textW / 2;
+ DrawString(screen, r, fontSmall, "fill the whole game area.", white, black);
+ r.y -= textH;
+ TTF_SizeText(fontSmall, "If you want to get a star,", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ DrawString(screen, r, fontSmall, "If you want to get a star,", white, black);
+ TTF_SizeText(fontNormal, "completed", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ r.y -= textH;
+ DrawString(screen, r, fontNormal, "completed", white, black);
+ //reload currentLevels button
+ r.x = reload.position.x;
+ r.y = reload.position.y;
+ SDL_BlitSurface(reload.picture, 0, screen, &r);
+ }
+ else
+ {
+ TTF_SizeText(fontNormal, "completed", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ r.y = arrowNext.position.y - arrowNext.picture->h - textH;
+ DrawString(screen, r, fontNormal, "completed", white, black);
+ }
+ if (currentLevelIndex != currentLevelCount - 1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+ }
+ else
+ {
+ TTF_SizeText(fontNormal, "game over", &textW, &textH);
+ r.x = (screen->w - textW) / 2;
+ r.y = arrowNext.position.y - arrowNext.picture->h - textH;
+ DrawString(screen, r, fontNormal, "game over", white, black);
+ //score
+ *str = 0;
+ sprintf(str, "you have made %d levels", currentTimeTScore);
+ TTF_SizeText(fontSmall, str, &textW, NULL);
+ r.x = screen->w / 2 - textW / 2;
+ r.y += textH;
+ DrawString(screen, r, fontSmall, str, white, black);
+ }
+ break;
+
+ case UserLevelLoading:
+ if (!screenBlurred)
+ {
+ BlurH(screen, 2);
+ BlurW(screen, 2);
+ screenBlurred = 1;
+ }
+ r.x = menuButton.position.x;
+ r.y = menuButton.position.y;
+ SDL_BlitSurface(menuButton.picture, 0, screen, &r);
+ TTF_SizeText(fontSmall, userLevelError, &textW, &textH);
+ r.y = arrowNext.position.y - arrowNext.picture->h - textH;
+ r.x = screen->w / 2 - textW / 2;
+ DrawString(screen, r, fontSmall, userLevelError, white, black);
+ break;
+
+ case TimeTrialMenu:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r); r.x = 50;
+ r.y = 0;
+ DrawString(screen, r, fontNormal, "time trial", white, black);
+ r.y = MAINMENU_ITEM_MARGIN;
+ r.x = TIME_TRIAL_MARGIN_LEFT;
+ for (i = 0; i < sizeof(timeTrialMenuItems)/sizeof(TimeTrialMenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, timeTrialMenuItems[i].name, NULL, &textH);
+ if (timeTrialMenuItems[i].mouseOver)
+ {
+ if (timeTrialMenuItems[i].mouseDown)
+ DrawString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ Darken(timeTrialMenuItems[i].color, 0.3), timeTrialMenuItems[i].color);
+ else
+ DrawString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ Darken(timeTrialMenuItems[i].color, 0.3), black);
+ }
+ else
+ DrawString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ timeTrialMenuItems[i].color, black);
+ if (timeTHighScores[timeTrialMenuItems[i].index] != 0)
+ {
+ *str = 0;
+ sprintf(str, "%d", timeTHighScores[timeTrialMenuItems[i].index]);
+ TTF_SizeText(fontNormal, str, &textW, NULL);
+ r.x = screen->w - textW - TIME_TRIAL_MARGIN_LEFT;
+ DrawString(screen, r, fontNormal, str, white, black);
+ r.x = TIME_TRIAL_MARGIN_LEFT;
+ }
+ r.y += textH;
+ }
+ break;
+
+ case AboutMenu:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ r.x = 50;
+ r.y = 0;
+ DrawString(screen, r, fontNormal, "about", white, black);
+ TTF_SizeText(fontNormal, "szabolevente", &textW, NULL);
+ r.x = screen->w / 2 - textW / 2;
+ r.y = 200;
+ aboutAnimation += 0xc3a3;
+ white.r = (aboutAnimation & 0xff0000) >> 16;
+ white.g = (aboutAnimation & 0xff00) >> 8;
+ white.b = (aboutAnimation & 0xff);
+ DrawString(screen, r, fontNormal, "szabolevente", white, black);
+ TTF_SizeText(fontNormal, "@ mail.com", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ r.y += textH;
+ DrawString(screen, r, fontNormal, "@ mail.com", white, black);
+ white.r = 255;
+ white.g = 255;
+ white.b = 255;
+ break;
+ default:
+ break;
+ }
+ SDL_Flip(screen);
+}
+
+///
+/// Sends a user event.
+///
+/// The previous wait time in milliseconds.
+/// User definied param.
+/// Time to wait until next call in millisecons.
+///
+Uint32 SendUserEventTick(Uint32 ms, void* param)
+{
+ SDL_Event ev;
+ ev.type = SDL_USEREVENT;
+ SDL_PushEvent(&ev);
+ return ms;
+}
+
+///
+/// Loads the game resources.
+///
+/// Returns 0 if succeess, 1 if error
+///
+int LoadResources()
+{
+ FILE *file;
+ if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
+ {
+ printf( "Unable to init SDL: %s\n", SDL_GetError());
+ return -1;
+ }
+ atexit(SDL_Quit);
+ if(TTF_Init()==-1)
+ {
+ printf("Unable to init TTF: %s\n", TTF_GetError());
+ return -1;
+ }
+ atexit(TTF_Quit);
+ if ((screen = SDL_SetVideoMode(480, 640, 0, SDL_HWSURFACE|SDL_DOUBLEBUF)) == NULL)
+ {
+ printf("Unable to set 480x640 video: %s\n", SDL_GetError());
+ return 1;
+ }
+ if(IMG_Init(IMG_INIT_TIF | IMG_INIT_JPG | IMG_INIT_PNG) == 0)
+ {
+ printf("Failed to init required support!\n");
+ }
+ atexit(IMG_Quit);
+ if ((fontTitle = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_BIG)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((fontNormal = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_NORMAL)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((fontSmall = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_SMALL)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((icon = IMG_Load("icon.bmp")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((arrowBack.pictureDefault = IMG_Load("left_arrow.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((arrowBack.pictureMouseOver = IMG_Load("left_arrow_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((starPic = IMG_Load("star.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((cMarkPic = IMG_Load("cMark.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((menuButton.pictureDefault = IMG_Load("menu.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((menuButton.pictureMouseOver = IMG_Load("menu_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((reload.pictureDefault = IMG_Load("reload.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((reload.pictureMouseOver = IMG_Load("reload_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ //Load default currentLevels
+ if ((file = fopen("defaultLevels.txt", "rt")) == NULL)
+ {
+ printf("Unable to load currentLevels\n");
+ return -1;
+ }
+ defaultLevels = NULL;
+ LoadLevelsFromFile(file, &defaultLevels, &defaultLevelCount);
+ if (!defaultLevels)
+ {
+ printf("Unable to parse currentLevels\n");
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+ //load time trial high scores
+ timeTHighScores[0] = 0;
+ timeTHighScores[1] = 0;
+ timeTHighScores[2] = 0;
+ if ((file = fopen("scores.txt", "rt")) != NULL)
+ {
+ fscanf(file, "%d,", &timeTHighScores[0]);
+ fscanf(file, "%d,", &timeTHighScores[1]);
+ fscanf(file, "%d", &timeTHighScores[2]);
+ fclose(file);
+ }
+ return 0;
+}
+
+///
+/// Unloads the game resources.
+///
+///
+void UnloadResources()
+{
+ int i;
+ SDL_RemoveTimer(userTimer);
+ SDL_FreeSurface(starPic);
+ SDL_FreeSurface(cMarkPic);
+ SDL_FreeSurface(screen);
+ TTF_CloseFont(fontTitle);
+ TTF_CloseFont(fontNormal);
+ TTF_CloseFont(fontSmall);
+ if (userLevels)
+ {
+ for (i = 0; i < userLevelCount; i++)
+ FreeLevelContent(&userLevels[i]);
+ free(userLevels);
+ }
+ if (defaultLevels)
+ {
+ for (i = 0; i < defaultLevelCount; i++)
+ FreeLevelContent(&defaultLevels[i]);
+ free(defaultLevels);
+ }
+}
+
+///
+/// Initialize game.
+///
+///
+///
+int Init()
+{
+ SDL_WM_SetCaption("Flow", "Flow");
+ SDL_WM_SetIcon(icon, NULL);
+ srand(SDL_GetTicks());
+
+ //Game vars
+ gameState = MainMenu;
+ exiting = 0;
+ loadUserLevel = 1;
+ currentLevels = defaultLevels;
+ userLevels = NULL;
+ currentLevelCount = defaultLevelCount;
+ currentLevelIndex = 0;
+ currentLevelSelectPage = 0;
+ levelSelectPageCount = 1 + (defaultLevelCount - 1 ) / 9;
+ aboutAnimation = 0;
+ isTimeTrialGame = 0;
+ currentTime = SDL_GetTicks();
+
+ //set button pictures
+ if ((arrowNext.pictureDefault = FlipH(arrowBack.pictureDefault)) == NULL)
+ {
+ printf("Unable to flip bitmap\n");
+ return -1;
+ }
+ if ((arrowNext.pictureMouseOver = FlipH(arrowBack.pictureMouseOver)) == NULL)
+ {
+ printf("Unable to flip bitmap\n");
+ return -1;
+ }
+ arrowPrev.pictureDefault = arrowBack.pictureDefault;
+ arrowPrev.pictureMouseOver = arrowBack.pictureMouseOver;
+ reload.picture = reload.pictureDefault;
+ arrowBack.picture = arrowBack.pictureDefault;
+ arrowNext.picture = arrowNext.pictureDefault;
+ arrowPrev.picture = arrowPrev.pictureDefault;
+ menuButton.picture = menuButton.pictureDefault;
+
+ //set button positions
+ arrowNext.position.x = screen->w / 2 - (int)(arrowNext.picture->w * 0.5) + LEVEL_CHANGE_ARROW_DIST;
+ arrowPrev.position.x = screen->w / 2 - (int)(arrowPrev.picture->w * 0.5) - LEVEL_CHANGE_ARROW_DIST;
+ menuButton.position.x = screen->w / 2 - menuButton.picture->w * 0.5;
+ return 0;
+}
+
+///
+/// Saves the loaded levels to file.
+///
+///
+void Save()
+{
+ FILE *file;
+ int i, j;
+ if ((file = fopen("defaultLevels.txt", "wt")) != NULL)
+ {
+ for (i = 0; i < defaultLevelCount; i++)
+ {
+ fprintf(file, "{");
+ for (j = 0; j < defaultLevels[i].flowCount; j++)
+ {
+ fprintf(file, "{%d,%d,%d,%d},",
+ defaultLevels[i].flows[j].firstElement->position.x + 1,
+ defaultLevels[i].flows[j].firstElement->position.y + 1,
+ defaultLevels[i].flows[j].lastElement->position.x + 1,
+ defaultLevels[i].flows[j].lastElement->position.y + 1);
+ }
+ if (i == defaultLevelCount - 1)
+ fprintf(file, "%d,%d,0}", defaultLevels[i].size, defaultLevels[i].state);
+ else
+ fprintf(file, "%d,%d,0}\n", defaultLevels[i].size, defaultLevels[i].state);
+ }
+ fclose(file);
+ }
+ if ((file = fopen("scores.txt", "wt")) != NULL)
+ {
+ fprintf(file, "%d,%d,%d", timeTHighScores[0], timeTHighScores[1], timeTHighScores[2]);
+ fclose(file);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ if(LoadResources() == -1)
+ return 1;
+ atexit(UnloadResources);
+ if(Init() == -1)
+ return 1;
+
+ //Main game loop
+ userTimer = SDL_AddTimer(400, SendUserEventTick, NULL);
+ while (!exiting)
+ {
+ ProcessEvents();
+ if (Update() == -1)
+ return 1;
+ Draw();
+ }
+ Save();
+ return 0;
+}
diff --git a/Flow/main.c b/Flow/main.c
new file mode 100644
index 0000000..cbde1bc
--- /dev/null
+++ b/Flow/main.c
@@ -0,0 +1,1982 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define FOR_EACH(element, first) \
+ for ((element) = (first); (element); (element) = (element)->next)
+
+typedef enum GameState
+{
+ MainMenu,
+ LevelSelectMenu,
+ TimeTrialMenu,
+ UserLevelLoading,
+ AboutMenu,
+ ActiveGame,
+ GameOver,
+ Exit
+} GameState;
+
+typedef enum FlowElementShape
+{
+ None = 0,
+ UpS = (1<<0),
+ RightS = (1<<1),
+ DownS = (1<<2),
+ LeftS = (1<<3),
+ EndS = (1<<4)
+} FlowElementShape;
+
+typedef enum FlowDirection
+{
+ FromFirst = 1<<0,
+ FromLast = 1<<1
+}FlowDirection;
+
+typedef struct FlowElement
+{
+ struct FlowElement *prev, *next;
+ SDL_Rect position;
+ FlowElementShape shape;
+} FlowElement;
+
+typedef struct Flow
+{
+ FlowElement *firstElement;
+ FlowElement *lastElement;
+ SDL_Color color;
+ int completed;
+ FlowDirection direction;
+} Flow;
+
+typedef enum LevelState
+{
+ Uncompleted, Completed, Starred
+} LevelState;
+
+typedef struct Level
+{
+ Flow *flows;
+ LevelState state;
+ unsigned int timeRecord;
+ int size, flowCount;
+} Level;
+
+typedef struct MenuItem
+{
+ char *name, mouseDown, mouseOver;
+ GameState gameState;
+ SDL_Color color;
+} MenuItem;
+
+typedef struct TimeTrialMenuItem
+{
+ char *name, mouseDown, mouseOver;
+ int index, time;
+ SDL_Color color;
+} TimeTrialMenuItem;
+
+typedef struct Button
+{
+ SDL_Rect position;
+ GameState gameState;
+ SDL_Surface *picture, *pictureDefault, *pictureMouseOver;
+} Button;
+
+typedef struct LevelTile
+{
+ SDL_Rect position;
+ char mouseDown;
+} LevelTile;
+
+typedef enum MouseButtonState
+{
+ Up, Down, JustUp, JustDown
+} MouseButtonState;
+
+const int
+ FONTSIZE_BIG = 130,
+ FONTSIZE_NORMAL = 50,
+ FONTSIZE_SMALL = 13,
+ MAINMENU_ITEM_MARGIN = 200,
+ LEVEL_TILE_MARGIN_TOP = 150,
+ LEVEL_TILE_PADDING = 5,
+ LEVEL_TILE_SIZE = 130,
+ GAME_AREA_SIZE = 400,
+ GAME_AREA_GRID_WIDTH = 3,
+ FLOW_SIZE_PERCENT = 27,
+ FLOW_END_SIZE_PERCENT = 54,
+ FLOW_BG_OPACITY = 7,
+ LEVEL_CHANGE_ARROW_DIST = 60,
+ TIME_TRIAL_MARGIN_LEFT = 50;
+const SDL_Color
+ LEVELTILE_COLOR = {96, 255, 47, 0},
+ GAME_AREA_GRID_COLOR = {0, 15, 0, 0},
+ FLOWCOLORS[] = {
+ 255, 0, 0, 0,
+ 255, 255, 0, 0, //yellow
+ 0, 255, 0, 0,
+ 0, 0, 255, 0,
+ 0, 255, 255, 0, //cyan
+ 255, 124, 0, 0, //orange
+ 140, 35, 163, 0,
+};
+SDL_TimerID userTimer;
+SDL_Surface *screen, *icon, *starPic, *cMarkPic;
+TTF_Font *fontTitle, *fontNormal, *fontSmall;
+GameState gameState;
+Level *currentLevels, *defaultLevels, *userLevels;
+int currentLevelIndex, currentLevelCount, defaultLevelCount, userLevelCount,
+ currentLevelSelectPage, levelSelectPageCount, innerFlowElementCount,
+ completedFlowCount ,currentTimeTTime, currentTimeTScore,
+ timeTHighScores[3], timeTScoreIndex;
+char exiting, screenBlurred, loadUserLevel, KeysDown[SDLK_LAST + 1] = {0},
+ *userLevelError = NULL, isTimeTrialGame;
+Uint32 currentTime;
+Uint32 aboutAnimation;
+MouseButtonState LMB;
+SDL_Rect mousePosition, mousePositionDown;
+MenuItem mainMenuItems[] = {
+ "arcade", 0, 0, LevelSelectMenu, {250,34,49},
+ "time trial", 0, 0, TimeTrialMenu, {255,191,31},
+ "user level", 0, 0, UserLevelLoading, {191,255,50},
+ "about", 0, 0, AboutMenu, {171,227,25},
+ "exit", 0, 0, Exit, {0, 196, 129}};
+Button arrowBack = {12, 33, 0, 0, MainMenu, NULL, NULL, NULL},
+ arrowNext = {0, 300, 0, 0, ActiveGame, NULL, NULL, NULL},
+ arrowPrev = {0, 300, 0, 0, LevelSelectMenu, NULL, NULL, NULL},
+ reload = {0, 300, 0, 0, ActiveGame, NULL, NULL, NULL},
+ menuButton = {0, 300, 0, 0, LevelSelectMenu, NULL, NULL, NULL};
+TimeTrialMenuItem timeTrialMenuItems[] = {
+ "30 sec", 0, 0, 0, 30, {250,34,49},
+ "60 sec", 0, 0, 1, 60, {255,191,31},
+ "90 sec", 0, 0, 2, 90, {191,255,50}};
+LevelTile levelTiles[9] = {0};
+FlowElement *flowElementStart;
+Flow *flowStart;
+
+///
+/// Gets the maximum of two int.
+///
+/// A number.
+/// A number.
+/// Returns the higher number
+__inline int Max(int a, int b) {
+ return a > b ? a : b;
+}
+
+///
+/// Draws a string on a screen at a position with specified foreground and background color.
+///
+/// The surface.
+/// The target rect.
+/// The font.
+/// The string.
+/// The foreground color.
+/// The background color.
+void DrawString(SDL_Surface *surface, SDL_Rect rect, TTF_Font *font, char *text, SDL_Color fg, SDL_Color bg)
+{
+ SDL_Surface *fontSurface = TTF_RenderText_Shaded(font, text, fg, bg);
+ if(fontSurface)
+ {
+ SDL_BlitSurface(fontSurface, 0, screen, &rect);
+ SDL_FreeSurface(fontSurface);
+ }
+}
+
+///
+/// Darkens the specified color.
+///
+/// The source color.
+/// The amount of change (0-1).
+/// Returns the changed color.
+SDL_Color Darken(SDL_Color color, double amount)
+{
+ SDL_Color ret;
+ ret.r = (int) Max(0, color.r - 255 * amount);
+ ret.g = (int) Max(0, color.g - 255 * amount);
+ ret.b = (int) Max(0, color.b - 255 * amount);
+ return ret;
+}
+
+///
+/// Converts SDL_Color to UInt32.
+///
+/// The source color.
+/// Returns the converted color value.
+Uint32 SDLColorTo32bit(SDL_Color color)
+{
+ Uint32 ret, t;
+ t = color.r;
+ ret = t << 24;
+ t = color.g;
+ ret |= t << 16;
+ t = color.b;
+ ret |= t << 8;
+ return ret |= 0xff;
+}
+
+///
+/// Sets the opacity of a color.
+///
+/// The source color.
+/// The opacity percent.
+/// Returns the changed color.
+Uint32 SetOpacity(Uint32 color, int percent)
+{
+ return ((color>>8)<<8) | (Uint32)((double)0xff * (double)percent / 100);
+}
+
+///
+/// Gets a pixel from the surface.
+///
+/// The surface.
+/// The x coordinate.
+/// The y coordinate.
+/// Returns the pixel.
+Uint32 GetPixel(SDL_Surface *surface, int x, int y)
+{
+ Uint8 bpp = surface->format->BytesPerPixel;
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
+ switch (bpp)
+ {
+ case 1:
+ return *p;
+ case 2:
+ return *(Uint16 *)p;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
+ return p[0] << 16 | p[1] << 8 | p[2];
+ else
+ return p[0] | p[1] << 8 | p[2] << 16;
+ case 4:
+ return *(Uint32 *)p;
+ default:
+ return 0;
+ }
+}
+
+///
+/// Sets a pixel on the surface.
+///
+/// The surface.
+/// The x coordinate.
+/// The y coordinate.
+/// The pixel.
+void SetPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ int bpp = surface->format->BytesPerPixel;
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
+ switch (bpp)
+ {
+ case 1:
+ *p = pixel;
+ break;
+ case 2:
+ *(Uint16 *)p = pixel;
+ break;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
+ p[0] = (pixel >> 16) & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = pixel & 0xff;
+ }
+ else {
+ p[0] = pixel & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = (pixel >> 16) & 0xff;
+ }
+ break;
+ case 4:
+ *(Uint32 *)p = pixel;
+ break;
+ default:
+ break;
+ }
+}
+
+///
+/// Flips the surface horizontally.
+///
+/// The surface.
+/// Returns the flipped surface.
+SDL_Surface *FlipH(SDL_Surface *surface)
+{
+ SDL_Surface *ret = NULL;
+ int x, y, rx, ry;
+ if(SDL_MUSTLOCK(surface))
+ SDL_LockSurface(surface);
+ ret = SDL_CreateRGBSurface(SDL_HWSURFACE, surface->w, surface->h,
+ surface->format->BitsPerPixel, surface->format->Rmask,
+ surface->format->Gmask, surface->format->Bmask, surface->format->Amask);
+ for(x = 0, rx = ret->w - 1; x < ret->w; x++, rx--)
+ for(y = 0, ry = ret->h - 1; y < ret->h; y++, ry--)
+ SetPixel(ret, rx, y, GetPixel(surface, x, y));
+ if(SDL_MUSTLOCK(surface))
+ SDL_UnlockSurface(surface);
+ return ret;
+}
+
+///
+/// Blurs the surface horizontally.
+///
+/// The surface.
+/// The blur amount.
+void BlurH(SDL_Surface *surface, int amount)
+{
+ int x, y, hits, oldPixel, newPixel, bpp;
+ Uint32 color, *newColors, r, g, b;
+ bpp = surface->format->BytesPerPixel;
+ newColors = (Uint32 *)malloc(sizeof(Uint32 *) * surface->w);
+ for (y = 0; y < surface->h; y++)
+ {
+ hits = 0;
+ r = g = b = 0;
+ for (x = -amount; x < surface->w; x++)
+ {
+ oldPixel = x - amount - 1;
+ if (oldPixel >= 0)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + oldPixel * bpp);
+ if (color != 0)
+ {
+ r -= (color & 0xff0000) >> 16;
+ g -= (color & 0xff00) >> 8;
+ b -= (color & 0xff);
+ }
+ hits--;
+ }
+ newPixel = x + amount;
+ if (newPixel < surface->w)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + newPixel * bpp);
+ if (color != 0)
+ {
+ r += (color & 0xff0000) >> 16;
+ g += (color & 0xff00) >> 8;
+ b += (color & 0xff);
+ }
+ hits++;
+ }
+ if (x >= 0)
+ {
+ newColors[x] = (((int)((double)r / hits)) << 16)
+ | (((int)((double)g / hits)) << 8)
+ | (int)((double)b / hits);
+ }
+ }
+ for (x = 0; x < surface->w; x++)
+ *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + x * bpp) = newColors[x];
+ }
+ free(newColors);
+}
+
+///
+/// Blurs the surface vertically.
+///
+/// The surface.
+/// The blur amount.
+void BlurW(SDL_Surface *surface, int amount)
+{
+ int x, y, hits, oldPixel, newPixel, bpp;
+ Uint32 color, *newColors, r, g, b;
+ bpp = surface->format->BytesPerPixel;
+ newColors = (Uint32 *)malloc(surface->h * sizeof(Uint32 *));
+ for (x = 0; x < surface->w; x++)
+ {
+ hits = 0;
+ r = g = b = 0;
+ for (y = -amount; y < surface->h; y++)
+ {
+ oldPixel = y - amount - 1;
+ if (oldPixel >= 0)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + oldPixel * surface->pitch + x * bpp);
+ if (color != 0)
+ {
+ r -= (color & 0xff0000) >> 16;
+ g -= (color & 0xff00) >> 8;
+ b -= (color & 0xff);
+ }
+ hits--;
+ }
+ newPixel = y + amount;
+ if (newPixel < surface->h)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + newPixel * surface->pitch + x * bpp);
+ if (color != 0)
+ {
+ r += (color & 0xff0000) >> 16;
+ g += (color & 0xff00) >> 8;
+ b += (color & 0xff);
+ }
+ hits++;
+ }
+ if (y >= 0)
+ {
+ newColors[y] = (((int)((double)r / hits)) << 16)
+ | (((int)((double)g / hits)) << 8)
+ | (int)((double)b / hits);
+ }
+ }
+ for (y = 0; y < surface->h; y++)
+ *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + x * bpp) = newColors[y];
+ }
+ free(newColors);
+}
+
+
+///
+/// Removes the flow element at the postition and removes the broken off FlowElements in a level.
+///
+/// The level.
+/// The x coordinate.
+/// The y coordinate.
+/// Determines whether to remove the FlowElement at the positon,
+/// or remove only the rest.
+/// Returns 1 if FlowElement can be removed or the position is free,
+/// 0 if the FlowElement at the position is the first or the last in a Flow.
+int RemoveFlowElement(Level *level, int xIn, int yIn, char removeThis)
+{
+ Flow *f;
+ FlowElement *fElem1, *fElem2;
+ int a, b, i;
+ for (i = 0; i < level->flowCount; i++)
+ {
+ f = &level->flows[i];
+ FOR_EACH(fElem1, f->firstElement)
+ {
+ if (fElem1->position.x == xIn && fElem1->position.y == yIn)
+ {
+ if (f->completed)
+ {
+ if (fElem1 == f->firstElement || fElem1 == f->lastElement)
+ {
+ if (removeThis)
+ return 0;
+ else
+ {
+ RemoveFlowElement(level, f->firstElement->next->position.x, f->firstElement->next->position.y, 1);
+ RemoveFlowElement(level, f->lastElement->prev->position.x, f->lastElement->prev->position.y, 1);
+ f->direction = (FlowDirection)(FromFirst | FromLast);
+ f->completed = 0;
+ return 1;
+ }
+ }
+ //remove from xy until end (remove the shorter part)
+ for (fElem2 = fElem1->next, a = 0; fElem2 != f->lastElement; fElem2 = fElem2->next, a++);
+ for (fElem2 = fElem1->prev, b = 0; fElem2 != f->firstElement; fElem2 = fElem2->prev, b++);
+ if (a > b)
+ {
+ if (!removeThis) //skip one
+ fElem1 = fElem1->prev;
+ fElem1->next->prev = f->firstElement;
+ f->firstElement->next = fElem1->next;
+ f->direction = (FlowDirection)FromLast;
+ f->completed = 0;
+ RemoveFlowElement(level, f->lastElement->position.x, f->lastElement->position.y, 0);
+ }
+ else
+ {
+ if (!removeThis)
+ fElem1 = fElem1->next;
+ fElem1->prev->next = f->lastElement;
+ f->lastElement->prev = fElem1->prev;
+ f->direction = (FlowDirection)FromFirst;
+ f->completed = 0;
+ RemoveFlowElement(level, f->firstElement->position.x, f->firstElement->position.y, 1);
+ }
+ return 1;
+ }
+ else //not completed
+ {
+ if (removeThis)
+ {
+ if (fElem1 == f->firstElement || fElem1 == f->lastElement)
+ return 0;
+ if (f->direction & FromFirst)
+ {
+ fElem1->prev->next = f->lastElement;
+ f->lastElement->prev = fElem1->prev;
+ }
+ else
+ {
+ fElem1->next->prev = f->firstElement;
+ f->firstElement->next = fElem1->next;
+ }
+ do
+ {
+ fElem2 = (f->direction & FromFirst) ? fElem1->next : fElem1->prev;
+ free(fElem1);
+ fElem1 = fElem2;
+ }
+ while (!(fElem1 == f->lastElement || fElem1 == f->firstElement));
+ return 1;
+ }
+ else
+ {
+ if (f->direction & FromFirst)
+ {
+ if (fElem1->next != NULL)
+ return RemoveFlowElement(level, fElem1->next->position.x, fElem1->next->position.y, 1);
+ else
+ return 1;
+ }
+ else if (fElem1->prev != NULL)
+ return RemoveFlowElement(level, fElem1->prev->position.x, fElem1->prev->position.y, 1);
+ else
+ return 1;
+ return 0;
+ }
+ }
+ }
+ }
+ }
+ return 1;
+}
+
+///
+/// Frees the level.
+///
+/// The level.
+void FreeLevelContent(Level *level)
+{
+ int i;
+ if (level == NULL)
+ return;
+ for (i = level->flowCount - 1; i >= 0; i--)
+ {
+ RemoveFlowElement(level, level->flows[i].firstElement->next->position.x, level->flows[i].firstElement->next->position.y, 1);
+ RemoveFlowElement(level, level->flows[i].lastElement->prev->position.x, level->flows[i].lastElement->prev->position.y, 1);
+ free(level->flows[i].firstElement);
+ free(level->flows[i].lastElement);
+ level->flowCount--;
+ }
+ free(level->flows);
+}
+
+///
+/// Loads the levels from file.
+///
+/// The file.
+/// The levels.
+/// The level count.
+void LoadLevelsFromFile(FILE *file, Level **levels, int *count)
+{
+ char c;
+ int i, flowColorIndex, countRet;
+ Flow *flow, *flowsTemp;
+ Level *levelRet = NULL, *levelTemp;
+ *levels = NULL;
+ *count = 0;
+
+ countRet = 1;
+ levelRet = (Level *)malloc(sizeof(Level));
+ for(c = fgetc(file); c != EOF && c != '{'; c = fgetc(file));
+ if (c == EOF)
+ return;
+ if((levelRet[countRet - 1].flows = (Flow *)malloc(sizeof(Flow))) == NULL)
+ return;
+ levelRet[countRet - 1].flowCount = 1;
+ flowColorIndex = 0;
+ while (1)
+ {
+ flow = &levelRet[countRet - 1].flows[levelRet[countRet - 1].flowCount - 1];
+ if((flow->firstElement = (FlowElement *)malloc(sizeof(FlowElement))) == NULL)
+ return;
+ if((flow->lastElement = (FlowElement *)malloc(sizeof(FlowElement))) == NULL)
+ return;
+ flow->firstElement->prev = NULL;
+ flow->firstElement->next = flow->lastElement;
+ flow->firstElement->shape = EndS;
+ flow->lastElement->next = NULL;
+ flow->lastElement->prev = flow->firstElement;
+ flow->lastElement->shape = EndS;
+ if(fscanf(file, "{%d,%d,%d,%d},",
+ &(flow->firstElement->position.x),
+ &(flow->firstElement->position.y),
+ &(flow->lastElement->position.x),
+ &(flow->lastElement->position.y)) < 4)
+ return;
+ flow->firstElement->position.x--;
+ flow->firstElement->position.y--;
+ flow->lastElement->position.x--;
+ flow->lastElement->position.y--;
+ flow->color = FLOWCOLORS[flowColorIndex++];
+ flow->completed = 0;
+ flow->direction = (FlowDirection)(FromFirst | FromLast);
+
+ if(fscanf(file, "%d,", &(levelRet[countRet - 1].size)) < 1) //there are more flows to read
+ {
+ if((flowsTemp = (Flow *)malloc(sizeof(Flow) * (levelRet[countRet - 1].flowCount + 1))) == NULL)
+ return;
+ for (i = 0; i < levelRet[countRet - 1].flowCount; i++)
+ flowsTemp[i] = levelRet[countRet - 1].flows[i];
+ free(levelRet[countRet - 1].flows);
+ levelRet[countRet - 1].flows = flowsTemp;
+ levelRet[countRet - 1].flowCount++;
+ }
+ else
+ {
+ if(fscanf(file, "%d,%d}",
+ &(levelRet[countRet - 1].state),
+ &(levelRet[countRet - 1].timeRecord)) < 2)
+ return;
+ for(c = fgetc(file); c != EOF && c != '{'; c = fgetc(file));
+ if (c == EOF)
+ {
+ *count = countRet;
+ *levels = levelRet;
+ return;
+ }
+ levelTemp = (Level *)malloc(sizeof(Level) * (countRet + 1));
+ for (i = 0; i < countRet; i++)
+ levelTemp[i] = levelRet[i];
+ free(levelRet);
+ levelRet = levelTemp;
+ countRet++;
+ if((levelRet[countRet - 1].flows = (Flow *)malloc(sizeof(Flow))) == NULL)
+ return;
+ levelRet[countRet - 1].flowCount = 1;
+ flowColorIndex = 0;
+ }
+ }
+}
+
+///
+/// Makes route from flowElemetStart to a position if it is possible
+///
+/// The x coordinate.
+/// The y coordinate.
+/// Returns -1 on error, 0 otherwise.
+int MakeRoute(int x, int y)
+{
+ int i, j, k, l;
+ FlowElement *fe1;
+ if (flowElementStart == NULL || flowStart == NULL)
+ return -1;
+ if (flowStart->completed)
+ {
+ if (flowElementStart->position.x == x && flowElementStart->position.y == y)
+ RemoveFlowElement(¤tLevels[currentLevelIndex], flowElementStart->position.x, flowElementStart->position.y, 0);
+ else //completed flow, but not released mouse yet
+ {
+ //find flowelement if it is under xy
+ FOR_EACH(fe1, flowStart->firstElement)
+ {
+ if (fe1->position.x == x && fe1->position.y == y)
+ {
+ if (fe1 == flowStart->firstElement->next &&
+ (fe1 != flowStart->lastElement || flowElementStart == flowStart->lastElement))
+ {
+ flowStart->direction = (FlowDirection)FromLast;
+ flowStart->completed = 0;
+ }
+ else if(fe1 == flowStart->lastElement->prev &&
+ (fe1 != flowStart->firstElement || flowElementStart == flowStart->firstElement))
+ {
+ flowStart->direction = (FlowDirection)FromFirst;
+ flowStart->completed = 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+ else //not completed
+ {
+ if (flowElementStart == flowStart->firstElement)
+ flowStart->direction = (FlowDirection)FromFirst;
+ else if (flowElementStart == flowStart->lastElement)
+ flowStart->direction = (FlowDirection)FromLast;
+ //starts from the first
+ if (flowStart->direction & FromFirst)
+ {
+ //remove flowElements after xy
+ FOR_EACH(fe1, flowStart->firstElement)
+ {
+ if (fe1->position.x == x && fe1->position.y == y) //xy is in flowStart
+ RemoveFlowElement(¤tLevels[currentLevelIndex], fe1->position.x, fe1->position.y, 0);
+ }
+ //add new FLowElements (xy is not in FLowStart)
+ fe1 = flowStart->lastElement->prev;
+ k = y - fe1->position.y;
+ l = x - fe1->position.x;
+ //advenced route not yet implemented, only left and right connection
+ if (!(((k == 0) && (l != 0)) || ((l == 0) && (k != 0))))
+ return 0;
+ i = fe1->position.y + (k == 0 ? 0 : (k > 0 ? 1 : -1));
+ do
+ {
+ j = fe1->position.x + (l == 0 ? 0 : (l > 0 ? 1 : -1));
+ do
+ {
+ //flow completed
+ if (flowStart->lastElement->position.x == j && flowStart->lastElement->position.y == i)
+ {
+ fe1->next = flowStart->lastElement;
+ flowStart->lastElement->prev = fe1;
+ flowStart->completed = 1;
+ flowStart->direction = (FlowDirection)(FromFirst | FromLast);
+ return 0;
+ }
+ if(RemoveFlowElement(¤tLevels[currentLevelIndex], j, i, 1))
+ {
+ if((fe1->next = (FlowElement *)malloc(sizeof(FlowElement))) == NULL)
+ return -1;
+ flowStart->lastElement->prev = fe1->next;
+ fe1->next->next = flowStart->lastElement;
+ fe1->next->prev = fe1;
+ fe1->next->position.x = j;
+ fe1->next->position.y = i;
+ fe1 = flowStart->lastElement->prev;
+ k = y - fe1->position.y;
+ l = x - fe1->position.x;
+ if (!(((k == 0) && (l != 0)) || ((l == 0) && (k != 0))))
+ return 0;
+ }
+ else
+ return 0;
+ j += (l == 0 ? 0 : (l > 0 ? 1 : -1));
+ }while (j != x + l);
+ i += (k == 0 ? 0 : (k > 0 ? 1 : -1));
+ }while(i != y + k);
+ }
+ //starts from the last
+ else
+ {
+ //remove flowElements after xy
+ FOR_EACH(fe1, flowStart->firstElement)
+ {
+ if (fe1->position.x == x && fe1->position.y == y)//xy is in FlowStart
+ RemoveFlowElement(¤tLevels[currentLevelIndex], fe1->position.x, fe1->position.y, 0);
+ }
+ //add new FLowElements (xy is not in FLowStart)
+ fe1 = flowStart->firstElement->next;
+ l = x - fe1->position.x;
+ k = y - fe1->position.y;
+ if (!(((k == 0) && (l != 0)) || ((l == 0) && (k != 0))))
+ return 0;
+ i = fe1->position.y + (k == 0 ? 0 : (k > 0 ? 1 : -1));
+ do
+ {
+ j = fe1->position.x + (l == 0 ? 0 : (l > 0 ? 1 : -1));
+ do
+ {
+ //flow completed
+ if (flowStart->firstElement->position.x == j && flowStart->firstElement->position.y == i)
+ {
+ fe1->prev = flowStart->firstElement;
+ flowStart->firstElement->next = fe1;
+ flowStart->completed = 1;
+ flowStart->direction = (FlowDirection)(FromFirst | FromLast);
+ return 0;
+ }
+ if(RemoveFlowElement(¤tLevels[currentLevelIndex], j, i, 1))
+ {
+ fe1->prev = (FlowElement *)malloc(sizeof(FlowElement));
+ if (fe1->prev == NULL)
+ return -1;
+ flowStart->firstElement->next = fe1->prev;
+ fe1->prev->prev = flowStart->firstElement;
+ fe1->prev->next = fe1;
+ fe1->prev->position.x = j;
+ fe1->prev->position.y = i;
+ fe1 = flowStart->firstElement->next;
+ l = x - fe1->position.x;
+ k = y - fe1->position.y;
+ if (!(((k == 0) && (l != 0)) || ((l == 0) && (k != 0))))
+ return 0;
+ }
+ else
+ return 0;
+ j += l == 0 ? 0 : (l > 0 ? 1 : -1);
+ }while (j != x + l);
+ i += k == 0 ? 0 : (k > 0 ? 1 : -1);
+ }while(i != y + k);
+ }
+ }
+ return 0;
+}
+
+///
+/// Determines whether the button is clicked.
+///
+/// The button.
+/// Returns 1 if clicked, otherwise 0
+int IsButtonClicked(Button *button)
+{
+ if (mousePosition.x >= button->position.x && mousePosition.x < button->position.x + button->picture->w &&
+ mousePosition.y >= button->position.y && mousePosition.y < button->position.y + button->picture->h)
+ if(LMB == JustUp)
+ return 1;
+ else
+ button->picture = button->pictureMouseOver;
+ else
+ button->picture = button->pictureDefault;
+ return 0;
+}
+
+///
+/// Determines whether a point is in the rectengle.
+///
+/// The x coordinate.
+/// The y coordinate.
+/// The width.
+/// The height.
+/// The point.
+/// Returns 1 if the point is in the rectangle, 0 otherwise.
+int InRect(int x, int y, int w, int h, SDL_Rect point)
+{
+ return point.x >= x && point.x < x + w && point.y >= y && point.y < y + h;
+}
+
+///
+/// Adds connection to a flowElement by setting it's shape
+///
+/// The FlowElement to add connection from.
+/// The FlowElement to add connection.
+void ShapeAddConnection(FlowElement *from, FlowElement *to)
+{
+ if (from->position.x > to->position.x)
+ {
+ to->shape = (FlowElementShape)(to->shape | RightS);
+ }
+ else if(from->position.x < to->position.x)
+ {
+ to->shape = (FlowElementShape)(to->shape | LeftS);
+ }
+ else if (from->position.y > to->position.y)
+ {
+ to->shape = (FlowElementShape)(to->shape | DownS);
+ }
+ else
+ {
+ to->shape = (FlowElementShape)(to->shape | UpS);
+ }
+}
+
+///
+/// Updates the FlowElement shapes
+///
+void UpdateShapes()
+{
+ Level *level;
+ FlowElement *feA;
+ int i;
+ level = ¤tLevels[currentLevelIndex];
+ for (i = 0; i < level->flowCount; i++)
+ {
+ FOR_EACH(feA, level->flows[i].firstElement)
+ {
+ feA->shape = (feA == level->flows[i].firstElement || feA == level->flows[i].lastElement) ? EndS : None;
+ if (level->flows[i].completed)
+ {
+ if (feA->next != NULL)
+ {
+ ShapeAddConnection(feA->next, feA);
+ }
+ if (feA->prev != NULL)
+ {
+ ShapeAddConnection(feA->prev, feA);
+ }
+ }
+ else
+ {
+ if (feA->next != NULL &&
+ ((feA->next != level->flows[i].lastElement) || (level->flows[i].direction & FromLast) && (feA != level->flows[i].firstElement)) &&
+ ((feA != level->flows[i].firstElement) || (level->flows[i].direction & FromFirst) && (feA->next != level->flows[i].lastElement)))
+ {
+ ShapeAddConnection(feA->next, feA);
+ }
+ if (feA->prev != NULL &&
+ ((feA->prev != level->flows[i].firstElement) || (level->flows[i].direction & FromFirst) && (feA != level->flows[i].lastElement)) &&
+ ((feA != level->flows[i].lastElement) || (level->flows[i].direction & FromLast) && (feA->prev != level->flows[i].firstElement)))
+ {
+ ShapeAddConnection(feA->prev, feA);
+ }
+ }
+ }
+ }
+}
+
+///
+/// Sets the current level.
+///
+/// Index of the currentLevels.
+void SetCurrentLevel(int levelIndex)
+{
+ int i;
+ if (currentLevelCount > 1)
+ {
+ if (levelIndex > currentLevelCount - 1)
+ levelIndex = currentLevelCount - 1;
+ else if (levelIndex < 0)
+ levelIndex = 0;
+ }
+ else
+ return;
+ currentLevelIndex = levelIndex;
+ innerFlowElementCount = 0;
+ completedFlowCount = 0;
+ //reset currentLevels
+ for (i = 0; i < currentLevels[levelIndex].flowCount; i++)
+ {
+ RemoveFlowElement(¤tLevels[levelIndex], currentLevels[levelIndex].flows[i].firstElement->position.x,
+ currentLevels[levelIndex].flows[i].firstElement->position.y, 0);
+ RemoveFlowElement(¤tLevels[levelIndex], currentLevels[levelIndex].flows[i].lastElement->position.x,
+ currentLevels[levelIndex].flows[i].lastElement->position.y, 0);
+ }
+ UpdateShapes();
+}
+
+///
+/// Processes the SDL events.
+///
+void ProcessEvents()
+{
+ SDL_Event event;
+ if(SDL_WaitEvent(&event))
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ exiting = 1;
+ break;
+ case SDL_KEYDOWN:
+ KeysDown[event.key.keysym.sym] = 1;
+ break;
+ case SDL_KEYUP:
+ KeysDown[event.key.keysym.sym] = 0;
+ break;
+ case SDL_MOUSEMOTION:
+ mousePosition.x = event.motion.x;
+ mousePosition.y = event.motion.y;
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ if (event.button.button == 1)
+ {
+ mousePositionDown.x = mousePosition.x;
+ mousePositionDown.y = mousePosition.y;
+ LMB = JustDown;
+ }
+ break;
+ case SDL_MOUSEBUTTONUP:
+ if (event.button.button == 1)
+ LMB = JustUp;
+ break;
+ default:
+ break;
+ }
+}
+
+void Draw();
+
+///
+/// Updates the game logic.
+///
+/// Returns -1 on error, 0 otherwise.
+int Update()
+{
+ int i, j, textW, textH, margin;
+ SDL_Rect v;
+ FlowElement *fElem1;
+ switch (gameState)
+ {
+ case MainMenu:
+ v.y = MAINMENU_ITEM_MARGIN;
+ for (i = 0; i < sizeof(mainMenuItems)/sizeof(MenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, mainMenuItems[i].name, &textW, &textH);
+ v.x = (screen->w - textW) / 2;
+ if (InRect(v.x, v.y, textW, textH, mousePosition))
+ {
+ mainMenuItems[i].mouseOver = 1;
+ if(LMB == JustDown)
+ mainMenuItems[i].mouseDown = 1;
+ else if(LMB == JustUp && mainMenuItems[i].mouseDown)
+ {
+ gameState = mainMenuItems[i].gameState;
+ mainMenuItems[i].mouseDown = 0;
+ Update();
+ break;
+ }
+ }
+ else
+ {
+ mainMenuItems[i].mouseOver = 0;
+ mainMenuItems[i].mouseDown = 0;
+ }
+ v.y += textH;
+ }
+ break;
+
+ case LevelSelectMenu:
+ currentLevels = defaultLevels;
+ currentLevelCount = defaultLevelCount;
+ arrowBack.gameState = MainMenu;
+ arrowNext.position.y = LEVEL_TILE_MARGIN_TOP + 3 * LEVEL_TILE_SIZE + 2 * LEVEL_TILE_PADDING + 20;
+ arrowPrev.position.y = arrowNext.position.y;
+ menuButton.gameState = LevelSelectMenu;
+ //Back button
+ if (IsButtonClicked(&arrowBack))
+ gameState = arrowBack.gameState;
+ if (currentLevelSelectPage > 0 && IsButtonClicked(&arrowPrev))
+ currentLevelSelectPage--;
+ if (currentLevelSelectPage < levelSelectPageCount - 1 && IsButtonClicked(&arrowNext))
+ currentLevelSelectPage++;
+ //Tiles
+ margin = (screen->w-(3 * LEVEL_TILE_SIZE + LEVEL_TILE_PADDING * 2)) / 2;
+ v.y = LEVEL_TILE_MARGIN_TOP;
+ for (i = 0; i < 3; i++)
+ {
+ v.x = margin;
+ for (j = 0; j < 3; j++)
+ {
+ if (InRect(v.x, v.y, LEVEL_TILE_SIZE, LEVEL_TILE_SIZE, mousePosition))
+ {
+ if(LMB == JustDown)
+ levelTiles[i * 3 + j].mouseDown = 1;
+ else
+ {
+ if(LMB == JustUp && levelTiles[i * 3 + j].mouseDown)
+ {
+ SetCurrentLevel(i * 3 + j + currentLevelSelectPage * 9);
+ gameState = ActiveGame;
+ Update();
+ arrowBack.gameState = LevelSelectMenu;
+ levelTiles[i * 3 + j].mouseDown = 0;
+ }
+ }
+ }
+ else
+ levelTiles[i * 3 + j].mouseDown = 0;
+ v.x += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ v.y += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ break;
+
+ case ActiveGame:
+ arrowNext.position.y = LEVEL_TILE_MARGIN_TOP + 3 * LEVEL_TILE_SIZE + 2 * LEVEL_TILE_PADDING + 20;
+ arrowPrev.position.y = arrowNext.position.y;
+ reload.position.y = arrowNext.position.y;
+ reload.position.x = screen->w / 2 - reload.picture->w / 2;
+ if (IsButtonClicked(&arrowBack))
+ gameState = arrowBack.gameState;
+ if (IsButtonClicked(&reload))
+ SetCurrentLevel(currentLevelIndex);
+ if (!isTimeTrialGame)
+ {
+ //next currentLevels button
+ if (currentLevelIndex != currentLevelCount - 1 && IsButtonClicked(&arrowNext))
+ {
+ SetCurrentLevel(currentLevelIndex + 1);
+ if (currentLevels == defaultLevels)
+ currentLevelSelectPage = (currentLevelIndex) / 9;
+ }
+ else if (currentLevelIndex > 0 && IsButtonClicked(&arrowPrev))
+ {
+ SetCurrentLevel(currentLevelIndex - 1);
+ if (currentLevels == defaultLevels)
+ currentLevelSelectPage = (currentLevelIndex) / 9;
+ }
+ }
+ else if (SDL_GetTicks() - 1000 > currentTime)
+ {
+ currentTime = SDL_GetTicks();
+ if (--currentTimeTTime == -1)
+ {
+ gameState = GameOver;
+ menuButton.gameState = TimeTrialMenu;
+ Update();
+ }
+ }
+ margin = (screen->w - GAME_AREA_SIZE) / 2;
+ if (InRect(margin, LEVEL_TILE_MARGIN_TOP, GAME_AREA_SIZE, GAME_AREA_SIZE, mousePosition))
+ {
+ //get clicked flow element
+ if(LMB == JustDown)
+ {
+ flowElementStart = NULL;
+ flowStart = NULL;
+ v.w = GAME_AREA_SIZE / currentLevels[currentLevelIndex].size; //flow element size
+ for (i = 0; i < currentLevels[currentLevelIndex].flowCount; i++)
+ {
+ FOR_EACH(fElem1, currentLevels[currentLevelIndex].flows[i].firstElement)
+ {
+ if(InRect(fElem1->position.x * v.w + margin,
+ fElem1->position.y * v.w + LEVEL_TILE_MARGIN_TOP, v.w, v.w, mousePosition))
+ {
+ flowStart = ¤tLevels[currentLevelIndex].flows[i];
+ flowElementStart = fElem1;
+ if (MakeRoute(flowElementStart->position.x, flowElementStart->position.y) == -1)
+ return -1; //memory error
+ UpdateShapes();
+ break;
+ }
+ }
+ }
+ }
+ else if (LMB == Down && flowElementStart != NULL && flowStart != NULL)
+ {
+ //convert mouse position to FlowElement posisiton
+ v.x = (double)(mousePosition.x - margin) / ((double)GAME_AREA_SIZE / currentLevels[currentLevelIndex].size);
+ v.y = (double)(mousePosition.y - LEVEL_TILE_MARGIN_TOP - 1) / ((double)GAME_AREA_SIZE / currentLevels[currentLevelIndex].size);
+ //connect FlowElements
+ if (MakeRoute(v.x, v.y) == -1)
+ return -1; //memory error
+ UpdateShapes();
+ //count completed Flows
+ completedFlowCount = 0;
+ innerFlowElementCount = 0;
+ for (i = 0; i < currentLevels[currentLevelIndex].flowCount; i++)
+ {
+ if (currentLevels[currentLevelIndex].flows[i].completed)
+ completedFlowCount++;
+ FOR_EACH(fElem1, currentLevels[currentLevelIndex].flows[i].firstElement)
+ innerFlowElementCount++;
+ innerFlowElementCount -= 2; //substract first and last
+ }
+ //check if game is over
+ if (currentLevels[currentLevelIndex].flowCount == completedFlowCount)
+ {
+ if (innerFlowElementCount + currentLevels[currentLevelIndex].flowCount * 2 ==
+ currentLevels[currentLevelIndex].size * currentLevels[currentLevelIndex].size)
+ currentLevels[currentLevelIndex].state = Starred;
+ else if (currentLevels[currentLevelIndex].state != Starred)
+ currentLevels[currentLevelIndex].state = Completed;
+ Draw(); //draw the last connection
+ if (isTimeTrialGame)
+ {
+ currentTimeTScore++;
+ SetCurrentLevel(rand() % defaultLevelCount);
+ }
+ else
+ {
+ gameState = GameOver;
+ Update();
+ screenBlurred = 0;
+ }
+ LMB = Up;
+ }
+ }
+ }
+ break;
+
+ case GameOver:
+ reload.position.x = screen->w / 2 - (int)(reload.picture->w * 0.5) - LEVEL_CHANGE_ARROW_DIST;
+ reload.position.y = 300;
+ arrowNext.position.y = 300;
+ arrowNext.gameState = ActiveGame;
+ if (IsButtonClicked(&menuButton))
+ gameState = menuButton.gameState;
+ if (!isTimeTrialGame)
+ {
+ //reload level button
+ if (currentLevels[currentLevelIndex].state != Starred && IsButtonClicked(&reload))
+ {
+ gameState = reload.gameState;
+ reload.picture = reload.pictureDefault;
+ SetCurrentLevel(currentLevelIndex);
+ }
+ //next level button
+ if (currentLevelIndex != currentLevelCount - 1 && IsButtonClicked(&arrowNext))
+ {
+ SetCurrentLevel(currentLevelIndex + 1);
+ currentLevelSelectPage = (currentLevelIndex + 1) / 10;
+ gameState = arrowNext.gameState;
+ Update();
+ }
+ }
+ else if (timeTHighScores[timeTScoreIndex] < currentTimeTScore)
+ timeTHighScores[timeTScoreIndex] = currentTimeTScore;
+ break;
+
+ case TimeTrialMenu:
+ currentLevels = defaultLevels;
+ arrowBack.gameState = MainMenu;
+ isTimeTrialGame = 0;
+ if (IsButtonClicked(&arrowBack))
+ gameState = arrowBack.gameState;
+ v.y = MAINMENU_ITEM_MARGIN;
+ v.x = TIME_TRIAL_MARGIN_LEFT;
+ for (i = 0; i < sizeof(timeTrialMenuItems)/sizeof(TimeTrialMenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, timeTrialMenuItems[i].name, &textW, &textH);
+ if (InRect(v.x, v.y, textW, textH, mousePosition))
+ {
+ timeTrialMenuItems[i].mouseOver = 1;
+ if(LMB == JustDown)
+ timeTrialMenuItems[i].mouseDown = 1;
+ else if(LMB == JustUp && timeTrialMenuItems[i].mouseDown)
+ {
+ gameState = ActiveGame;
+ timeTrialMenuItems[i].mouseDown = 0;
+ timeTrialMenuItems[i].mouseOver = 0;
+ currentTimeTTime = timeTrialMenuItems[i].time + 1;
+ timeTScoreIndex = timeTrialMenuItems[i].index;
+ isTimeTrialGame = 1;
+ currentTimeTScore = 0;
+ SetCurrentLevel(rand() % defaultLevelCount);
+ arrowBack.gameState = TimeTrialMenu;
+ Update();
+ }
+ }
+ else
+ {
+ timeTrialMenuItems[i].mouseDown = 0;
+ timeTrialMenuItems[i].mouseOver = 0;
+ }
+ v.y += textH;
+ }
+ break;
+
+ case UserLevelLoading:
+ if (loadUserLevel)
+ {
+ FILE *file;
+ loadUserLevel = 0;
+ menuButton.gameState = MainMenu;
+ if ((file = fopen("userLevels.txt", "rt")) == NULL)
+ {
+ userLevelError = "\"userLevels.txt\" could not be loaded.";
+ userLevels = NULL;
+ userLevelCount = 0;
+ }
+ else
+ {
+ if (userLevels) //free previously loaded currentLevels
+ {
+ for (i = 0; i < userLevelCount; i++)
+ FreeLevelContent(&userLevels[i]);
+ free(userLevels);
+ }
+ LoadLevelsFromFile(file, &userLevels, &userLevelCount);
+ if (!userLevels)
+ userLevelError = "\"userLevels.txt\" contains wrong format.";
+ else
+ {
+ currentLevels = userLevels;
+ currentLevelCount = userLevelCount;
+ SetCurrentLevel(0);
+ loadUserLevel = 1;
+ gameState = ActiveGame;
+ Update();
+ }
+ fclose(file);
+ }
+ screenBlurred = 0;
+ }
+ if (IsButtonClicked(&menuButton))
+ {
+ gameState = menuButton.gameState;
+ loadUserLevel = 1;
+ }
+ break;
+
+ case AboutMenu:
+ if (IsButtonClicked(&arrowBack))
+ gameState = arrowBack.gameState;
+ break;
+
+ case Exit:
+ exiting = 1;
+ break;
+ }
+ switch (LMB)
+ {
+ case JustUp:
+ LMB = Up;
+ break;
+ case JustDown:
+ LMB = Down;
+ break;
+ default:
+ break;
+ }
+ if (KeysDown[SDLK_ESCAPE])
+ exiting = 1;
+ return 0;
+}
+
+///
+/// Draws the game.
+///
+void Draw()
+{
+ SDL_Color white = {255,255,255};
+ SDL_Color black = {0,0,0};
+ char str[60];
+ int i, j, k, textW, textH, margin;
+ SDL_Rect r = {0, 0, 0, 0};
+ Flow *f;
+ FlowElement *fElem1;
+ switch (gameState)
+ {
+ case MainMenu:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ TTF_SizeText(fontTitle,"flow", &textW, NULL);
+ r.x = (screen->w - textW) / 2;
+ DrawString(screen, r, fontTitle, "flow", white, black);
+ r.y = MAINMENU_ITEM_MARGIN;
+ for (i = 0; i < sizeof(mainMenuItems)/sizeof(MenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, mainMenuItems[i].name, &textW, &textH);
+ r.x = (screen->w - textW) / 2;
+ if (mainMenuItems[i].mouseOver)
+ {
+ if (mainMenuItems[i].mouseDown)
+ DrawString(screen, r, fontNormal, mainMenuItems[i].name, Darken(mainMenuItems[i].color, 0.3), mainMenuItems[i].color);
+ else
+ DrawString(screen, r, fontNormal, mainMenuItems[i].name, Darken(mainMenuItems[i].color, 0.3), black);
+ }
+ else
+ DrawString(screen, r, fontNormal, mainMenuItems[i].name, mainMenuItems[i].color, black);
+ r.y += textH;
+ }
+ break;
+
+ case LevelSelectMenu:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ if (currentLevelSelectPage > 0)
+ {
+ r.x = arrowPrev.position.x;
+ r.y = arrowPrev.position.y;
+ SDL_BlitSurface(arrowPrev.picture, 0, screen, &r);
+ }
+ if (currentLevelSelectPage < levelSelectPageCount - 1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+
+ //current/all LevelTile page
+ *str = 0;
+ sprintf(str, "%d/%d", currentLevelSelectPage + 1, levelSelectPageCount);
+ TTF_SizeText(fontSmall, str, &textW, NULL);
+ r.x = screen->w / 2 - textW / 2;
+ r.y = LEVEL_TILE_MARGIN_TOP - 30;
+ DrawString(screen, r, fontSmall, str, white, black);
+
+ //Tiles
+ margin = (screen->w - (3 * LEVEL_TILE_SIZE + LEVEL_TILE_PADDING * 2)) / 2;
+ r.y = LEVEL_TILE_MARGIN_TOP;
+ for (i = 0; i < 3; i++)
+ {
+ r.x = margin;
+ for (j = 0; j < 3; j++)
+ {
+ sprintf(str, "%d", i * 3 + j + 1 + currentLevelSelectPage * 9);
+ TTF_SizeText(fontNormal, str, &textW, &textH);
+ if (levelTiles[i * 3 + j].mouseDown)
+ {
+ boxColor(screen, r.x, r.y, r.x + LEVEL_TILE_SIZE, r.y + LEVEL_TILE_SIZE,
+ SDLColorTo32bit(Darken(LEVELTILE_COLOR, 0.3)));
+ r.x += (LEVEL_TILE_SIZE - textW) / 2;
+ r.y += (LEVEL_TILE_SIZE - textH) / 2;
+ DrawString(screen, r, fontNormal, str, black, Darken(LEVELTILE_COLOR, 0.3));
+ r.x -= (LEVEL_TILE_SIZE - textW) / 2;
+ r.y -= (LEVEL_TILE_SIZE - textH) / 2;
+ }
+ else
+ {
+ boxColor(screen, r.x, r.y, r.x + LEVEL_TILE_SIZE, r.y + LEVEL_TILE_SIZE,
+ SDLColorTo32bit(LEVELTILE_COLOR));
+ r.x += (LEVEL_TILE_SIZE - textW) / 2;
+ r.y += (LEVEL_TILE_SIZE - textH) / 2;
+ DrawString(screen, r, fontNormal, str, black, LEVELTILE_COLOR);
+ r.x -= (LEVEL_TILE_SIZE - textW) / 2;
+ r.y -= (LEVEL_TILE_SIZE - textH) / 2;
+ }
+ //level complete indicator
+ if (i * 3 + j + currentLevelSelectPage * 9 < currentLevelCount)
+ {
+ r.x += LEVEL_TILE_SIZE - cMarkPic->w - 3;
+ r.y += LEVEL_TILE_SIZE - cMarkPic->h - 3;
+ switch (currentLevels[i * 3 + j + currentLevelSelectPage * 9].state)
+ {
+ case Completed:
+ SDL_BlitSurface(cMarkPic, 0, screen, &r);
+ break;
+ case Starred:
+ SDL_BlitSurface(starPic, 0, screen, &r);
+ break;
+ default:
+ break;
+ }
+ r.y -= LEVEL_TILE_SIZE - cMarkPic->h - 3;
+ r.x -= LEVEL_TILE_SIZE - cMarkPic->w - 3;
+ }
+ r.x += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ r.y += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ r.x = 50;
+ r.y = 0;
+ DrawString(screen, r, fontNormal, "choose levels", white, black);
+ break;
+
+ case ActiveGame:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ //back button
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ if (!isTimeTrialGame)
+ {
+ //previous currentLevels button
+ if (currentLevelIndex > 0)
+ {
+ r.x = arrowPrev.position.x;
+ r.y = arrowPrev.position.y;
+ SDL_BlitSurface(arrowPrev.picture, 0, screen, &r);
+ }
+ //next currentLevels button
+ if (currentLevelIndex != currentLevelCount - 1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+ }
+ else
+ {
+ //time left
+ *str = 0;
+ sprintf(str, "%d", currentTimeTTime);
+ TTF_SizeText(fontSmall, str, &textW, &textH);
+ r.x = (screen->w + GAME_AREA_SIZE) / 2 - 20;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ DrawString(screen, r, fontSmall, str, white, black);
+ //current score
+ *str = 0;
+ sprintf(str, "completed: %d", currentTimeTScore);
+ TTF_SizeText(fontSmall, str, &textW, &textH);
+ r.x = (screen->w + textW) / 2;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ DrawString(screen, r, fontSmall, str, white, black);
+ }
+ //reload currentLevels button
+ r.x = reload.position.x;
+ r.y = reload.position.y;
+ SDL_BlitSurface(reload.picture, 0, screen, &r);
+ margin = (screen->w - GAME_AREA_SIZE)/2;
+ //comleted/all flow count
+ *str = 0;
+ sprintf(str, "flows: %d/%d", completedFlowCount, currentLevels[currentLevelIndex].flowCount);
+ TTF_SizeText(fontSmall, str, &textW, &textH);
+ r.x = margin;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ DrawString(screen, r, fontSmall, str, white, black);
+ //percent
+ *str = 0;
+ sprintf(str, "%d%%",
+ (int)((double)(innerFlowElementCount + completedFlowCount) * 100.0 /
+ (double)(currentLevels[currentLevelIndex].size * currentLevels[currentLevelIndex].size - currentLevels[currentLevelIndex].flowCount)));
+ r.x = margin + 100;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ DrawString(screen, r, fontSmall, str, white, black);
+ //currentLevels state
+ r.x = screen->w - cMarkPic->w - 10;
+ r.y = 30;
+ switch (currentLevels[currentLevelIndex].state)
+ {
+ case Completed:
+ SDL_BlitSurface(cMarkPic, 0, screen, &r);
+ break;
+ case Starred:
+ SDL_BlitSurface(starPic, 0, screen, &r);
+ break;
+ default:
+ break;
+ }
+ //horizontal grid
+ for (i = 0; i <= currentLevels[currentLevelIndex].size; i++)
+ {
+ r.x = margin - GAME_AREA_GRID_WIDTH;
+ r.w = GAME_AREA_GRID_WIDTH + currentLevels[currentLevelIndex].size *
+ ((GAME_AREA_SIZE - (currentLevels[currentLevelIndex].size - 1) *
+ GAME_AREA_GRID_WIDTH) / currentLevels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH) - 1;
+ r.y = LEVEL_TILE_MARGIN_TOP - GAME_AREA_GRID_WIDTH +
+ i * ((GAME_AREA_SIZE - (currentLevels[currentLevelIndex].size - 1) *
+ GAME_AREA_GRID_WIDTH) / currentLevels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH);
+ r.h = GAME_AREA_GRID_WIDTH - 1;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SDLColorTo32bit(GAME_AREA_GRID_COLOR));
+ }
+ //vertical grid
+ for (i = 0; i <= currentLevels[currentLevelIndex].size; i++)
+ {
+ r.x = margin - GAME_AREA_GRID_WIDTH +
+ i * ((GAME_AREA_SIZE - (currentLevels[currentLevelIndex].size - 1) *
+ GAME_AREA_GRID_WIDTH) / currentLevels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH);
+ r.w = GAME_AREA_GRID_WIDTH - 1;
+ r.y = LEVEL_TILE_MARGIN_TOP - GAME_AREA_GRID_WIDTH;
+ r.h = GAME_AREA_SIZE + GAME_AREA_GRID_WIDTH - 1;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SDLColorTo32bit(GAME_AREA_GRID_COLOR));
+ }
+ //Flows
+ r.w = (GAME_AREA_SIZE - (currentLevels[currentLevelIndex].size - 1) * GAME_AREA_GRID_WIDTH) / currentLevels[currentLevelIndex].size - 1;
+ r.h = r.w;
+ i = r.w * FLOW_SIZE_PERCENT / 100.0; //current flow width
+ j = r.w * FLOW_END_SIZE_PERCENT / 100.0; //current flow end width
+ for (k = 0; k < currentLevels[currentLevelIndex].flowCount; k++)
+ {
+ f = ¤tLevels[currentLevelIndex].flows[k];
+ FOR_EACH(fElem1, currentLevels[currentLevelIndex].flows[k].firstElement)
+ {
+ r.x = fElem1->position.x * (r.w + GAME_AREA_GRID_WIDTH + 1) + margin;
+ r.y = fElem1->position.y * (r.w + GAME_AREA_GRID_WIDTH + 1) + LEVEL_TILE_MARGIN_TOP;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SetOpacity(SDLColorTo32bit(f->color), FLOW_BG_OPACITY));
+ if (fElem1->shape & UpS)
+ {
+ boxColor(screen, r.x+(r.w - i) / 2, r.y, r.x + (r.w - i) / 2 + i,
+ r.y +(r.h - i) / 2 + i, SDLColorTo32bit(f->color));
+ }
+ //overlap grid
+ if (fElem1->shape & DownS)
+ {
+ boxColor(screen, r.x + (r.w - i) / 2, r.y + r.w + GAME_AREA_GRID_WIDTH,
+ r.x+(r.w - i) / 2 + i, r.y + (r.h - i) / 2, SDLColorTo32bit(f->color));
+ }
+ //overlap grid
+ if (fElem1->shape & RightS)
+ {
+ boxColor(screen, r.x + (r.w - i) / 2, r.y +(r.h - i) / 2 + i, r.x + r.w +
+ GAME_AREA_GRID_WIDTH, r.y + (r.h - i) / 2, SDLColorTo32bit(f->color));
+ }
+ if (fElem1->shape & LeftS)
+ {
+ boxColor(screen, r.x, r.y +(r.h - i)/2, r.x+(r.w - i) / 2 + i,
+ r.y +(r.h - i) / 2 + i, SDLColorTo32bit(f->color));
+ }
+ if (fElem1->shape & EndS)
+ {
+ boxColor(screen, r.x + (r.w - j) / 2, r.y +(r.h - j) / 2, r.x+
+ (r.w - j) / 2 + j, r.y +(r.h - j) / 2 +
+ j, SDLColorTo32bit(f->color));
+ }
+ }
+ }
+ //print level name
+ *str = 0;
+ sprintf(str, "level %d", currentLevelIndex + 1);
+ r.x = 50;
+ r.y = 0;
+ DrawString(screen, r, fontNormal, str, white, black);
+ break;
+
+ case GameOver:
+ //blur previous screen
+ if (!screenBlurred)
+ {
+ BlurH(screen, 2);
+ BlurW(screen, 2);
+ screenBlurred = 1;
+ }
+ r.x = menuButton.position.x;
+ r.y = menuButton.position.y;
+ SDL_BlitSurface(menuButton.picture, 0, screen, &r);
+ if (!isTimeTrialGame)
+ {
+ if (currentLevels[currentLevelIndex].state != Starred)
+ {
+ TTF_SizeText(fontSmall, "fill the whole game area.", &textW, &textH);
+ r.y = arrowNext.position.y - arrowNext.picture->h - textH;
+ r.x = screen->w / 2 - textW / 2;
+ DrawString(screen, r, fontSmall, "fill the whole game area.", white, black);
+ r.y -= textH;
+ TTF_SizeText(fontSmall, "If you want to get a star,", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ DrawString(screen, r, fontSmall, "If you want to get a star,", white, black);
+ TTF_SizeText(fontNormal, "completed", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ r.y -= textH;
+ DrawString(screen, r, fontNormal, "completed", white, black);
+ //reload currentLevels button
+ r.x = reload.position.x;
+ r.y = reload.position.y;
+ SDL_BlitSurface(reload.picture, 0, screen, &r);
+ }
+ else
+ {
+ TTF_SizeText(fontNormal, "completed", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ r.y = arrowNext.position.y - arrowNext.picture->h - textH;
+ DrawString(screen, r, fontNormal, "completed", white, black);
+ }
+ if (currentLevelIndex != currentLevelCount - 1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+ }
+ else
+ {
+ TTF_SizeText(fontNormal, "game over", &textW, &textH);
+ r.x = (screen->w - textW) / 2;
+ r.y = arrowNext.position.y - arrowNext.picture->h - textH;
+ DrawString(screen, r, fontNormal, "game over", white, black);
+ //score
+ *str = 0;
+ sprintf(str, "you have made %d levels", currentTimeTScore);
+ TTF_SizeText(fontSmall, str, &textW, NULL);
+ r.x = screen->w / 2 - textW / 2;
+ r.y += textH;
+ DrawString(screen, r, fontSmall, str, white, black);
+ }
+ break;
+
+ case UserLevelLoading:
+ if (!screenBlurred)
+ {
+ BlurH(screen, 2);
+ BlurW(screen, 2);
+ screenBlurred = 1;
+ }
+ r.x = menuButton.position.x;
+ r.y = menuButton.position.y;
+ SDL_BlitSurface(menuButton.picture, 0, screen, &r);
+ TTF_SizeText(fontSmall, userLevelError, &textW, &textH);
+ r.y = arrowNext.position.y - arrowNext.picture->h - textH;
+ r.x = screen->w / 2 - textW / 2;
+ DrawString(screen, r, fontSmall, userLevelError, white, black);
+ break;
+
+ case TimeTrialMenu:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r); r.x = 50;
+ r.y = 0;
+ DrawString(screen, r, fontNormal, "time trial", white, black);
+ r.y = MAINMENU_ITEM_MARGIN;
+ r.x = TIME_TRIAL_MARGIN_LEFT;
+ for (i = 0; i < sizeof(timeTrialMenuItems)/sizeof(TimeTrialMenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, timeTrialMenuItems[i].name, NULL, &textH);
+ if (timeTrialMenuItems[i].mouseOver)
+ {
+ if (timeTrialMenuItems[i].mouseDown)
+ DrawString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ Darken(timeTrialMenuItems[i].color, 0.3), timeTrialMenuItems[i].color);
+ else
+ DrawString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ Darken(timeTrialMenuItems[i].color, 0.3), black);
+ }
+ else
+ DrawString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ timeTrialMenuItems[i].color, black);
+ if (timeTHighScores[timeTrialMenuItems[i].index] != 0)
+ {
+ *str = 0;
+ sprintf(str, "%d", timeTHighScores[timeTrialMenuItems[i].index]);
+ TTF_SizeText(fontNormal, str, &textW, NULL);
+ r.x = screen->w - textW - TIME_TRIAL_MARGIN_LEFT;
+ DrawString(screen, r, fontNormal, str, white, black);
+ r.x = TIME_TRIAL_MARGIN_LEFT;
+ }
+ r.y += textH;
+ }
+ break;
+
+ case AboutMenu:
+ SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ r.x = 50;
+ r.y = 0;
+ DrawString(screen, r, fontNormal, "about", white, black);
+ TTF_SizeText(fontNormal, "szabolevente", &textW, NULL);
+ r.x = screen->w / 2 - textW / 2;
+ r.y = 200;
+ aboutAnimation += 0xc3a3;
+ white.r = (aboutAnimation & 0xff0000) >> 16;
+ white.g = (aboutAnimation & 0xff00) >> 8;
+ white.b = (aboutAnimation & 0xff);
+ DrawString(screen, r, fontNormal, "szabolevente", white, black);
+ TTF_SizeText(fontNormal, "@ mail.com", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ r.y += textH;
+ DrawString(screen, r, fontNormal, "@ mail.com", white, black);
+ white.r = 255;
+ white.g = 255;
+ white.b = 255;
+ break;
+ default:
+ break;
+ }
+ SDL_Flip(screen);
+}
+
+///
+/// Sends a user event.
+///
+/// The previous wait time in milliseconds.
+/// User definied param.
+/// Time to wait until next call in millisecons.
+Uint32 SendUserEventTick(Uint32 ms, void* param)
+{
+ SDL_Event ev;
+ ev.type = SDL_USEREVENT;
+ SDL_PushEvent(&ev);
+ return ms;
+}
+
+///
+/// Loads the game resources.
+///
+/// Returns 0 if succeess, 1 if error
+int LoadResources()
+{
+ FILE *file;
+ if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
+ {
+ printf( "Unable to init SDL: %s\n", SDL_GetError());
+ return -1;
+ }
+ atexit(SDL_Quit);
+ if(TTF_Init()==-1)
+ {
+ printf("Unable to init TTF: %s\n", TTF_GetError());
+ return -1;
+ }
+ atexit(TTF_Quit);
+ if ((screen = SDL_SetVideoMode(480, 640, 0, SDL_HWSURFACE|SDL_DOUBLEBUF)) == NULL)
+ {
+ printf("Unable to set 480x640 video: %s\n", SDL_GetError());
+ return 1;
+ }
+ if(IMG_Init(IMG_INIT_TIF | IMG_INIT_JPG | IMG_INIT_PNG) == 0)
+ {
+ printf("Failed to init required support!\n");
+ }
+ atexit(IMG_Quit);
+ if ((fontTitle = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_BIG)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((fontNormal = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_NORMAL)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((fontSmall = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_SMALL)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((icon = IMG_Load("icon.bmp")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((arrowBack.pictureDefault = IMG_Load("left_arrow.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((arrowBack.pictureMouseOver = IMG_Load("left_arrow_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((starPic = IMG_Load("star.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((cMarkPic = IMG_Load("cMark.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((menuButton.pictureDefault = IMG_Load("menu.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((menuButton.pictureMouseOver = IMG_Load("menu_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((reload.pictureDefault = IMG_Load("reload.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((reload.pictureMouseOver = IMG_Load("reload_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ //Load default currentLevels
+ if ((file = fopen("defaultLevels.txt", "rt")) == NULL)
+ {
+ printf("Unable to load currentLevels\n");
+ return -1;
+ }
+ defaultLevels = NULL;
+ LoadLevelsFromFile(file, &defaultLevels, &defaultLevelCount);
+ if (!defaultLevels)
+ {
+ printf("Unable to parse currentLevels\n");
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+ //load time trial high scores
+ timeTHighScores[0] = 0;
+ timeTHighScores[1] = 0;
+ timeTHighScores[2] = 0;
+ if ((file = fopen("scores.txt", "rt")) != NULL)
+ {
+ fscanf(file, "%d,", &timeTHighScores[0]);
+ fscanf(file, "%d,", &timeTHighScores[1]);
+ fscanf(file, "%d", &timeTHighScores[2]);
+ fclose(file);
+ }
+ return 0;
+}
+
+///
+/// Unloads the game resources.
+///
+void UnloadResources()
+{
+ int i;
+ SDL_RemoveTimer(userTimer);
+ SDL_FreeSurface(starPic);
+ SDL_FreeSurface(cMarkPic);
+ SDL_FreeSurface(screen);
+ TTF_CloseFont(fontTitle);
+ TTF_CloseFont(fontNormal);
+ TTF_CloseFont(fontSmall);
+ if (userLevels)
+ {
+ for (i = 0; i < userLevelCount; i++)
+ FreeLevelContent(&userLevels[i]);
+ free(userLevels);
+ }
+ if (defaultLevels)
+ {
+ for (i = 0; i < defaultLevelCount; i++)
+ FreeLevelContent(&defaultLevels[i]);
+ free(defaultLevels);
+ }
+}
+
+///
+/// Initialize game.
+///
+///
+int Init()
+{
+ SDL_WM_SetCaption("Flow", "Flow");
+ SDL_WM_SetIcon(icon, NULL);
+ srand(SDL_GetTicks());
+
+ //Game vars
+ gameState = MainMenu;
+ exiting = 0;
+ loadUserLevel = 1;
+ currentLevels = defaultLevels;
+ userLevels = NULL;
+ currentLevelCount = defaultLevelCount;
+ currentLevelIndex = 0;
+ currentLevelSelectPage = 0;
+ levelSelectPageCount = 1 + (defaultLevelCount - 1 ) / 9;
+ aboutAnimation = 0;
+ isTimeTrialGame = 0;
+ currentTime = SDL_GetTicks();
+
+ //set button pictures
+ if ((arrowNext.pictureDefault = FlipH(arrowBack.pictureDefault)) == NULL)
+ {
+ printf("Unable to flip bitmap\n");
+ return -1;
+ }
+ if ((arrowNext.pictureMouseOver = FlipH(arrowBack.pictureMouseOver)) == NULL)
+ {
+ printf("Unable to flip bitmap\n");
+ return -1;
+ }
+ arrowPrev.pictureDefault = arrowBack.pictureDefault;
+ arrowPrev.pictureMouseOver = arrowBack.pictureMouseOver;
+ reload.picture = reload.pictureDefault;
+ arrowBack.picture = arrowBack.pictureDefault;
+ arrowNext.picture = arrowNext.pictureDefault;
+ arrowPrev.picture = arrowPrev.pictureDefault;
+ menuButton.picture = menuButton.pictureDefault;
+
+ //set button positions
+ arrowNext.position.x = screen->w / 2 - (int)(arrowNext.picture->w * 0.5) + LEVEL_CHANGE_ARROW_DIST;
+ arrowPrev.position.x = screen->w / 2 - (int)(arrowPrev.picture->w * 0.5) - LEVEL_CHANGE_ARROW_DIST;
+ menuButton.position.x = screen->w / 2 - menuButton.picture->w * 0.5;
+ return 0;
+}
+
+///
+/// Saves the loaded levels to file.
+///
+void Save()
+{
+ FILE *file;
+ int i, j;
+ if ((file = fopen("defaultLevels.txt", "wt")) != NULL)
+ {
+ for (i = 0; i < defaultLevelCount; i++)
+ {
+ fprintf(file, "{");
+ for (j = 0; j < defaultLevels[i].flowCount; j++)
+ {
+ fprintf(file, "{%d,%d,%d,%d},",
+ defaultLevels[i].flows[j].firstElement->position.x + 1,
+ defaultLevels[i].flows[j].firstElement->position.y + 1,
+ defaultLevels[i].flows[j].lastElement->position.x + 1,
+ defaultLevels[i].flows[j].lastElement->position.y + 1);
+ }
+ if (i == defaultLevelCount - 1)
+ fprintf(file, "%d,%d,0}", defaultLevels[i].size, defaultLevels[i].state);
+ else
+ fprintf(file, "%d,%d,0}\n", defaultLevels[i].size, defaultLevels[i].state);
+ }
+ fclose(file);
+ }
+ if ((file = fopen("scores.txt", "wt")) != NULL)
+ {
+ fprintf(file, "%d,%d,%d", timeTHighScores[0], timeTHighScores[1], timeTHighScores[2]);
+ fclose(file);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ if(LoadResources() == -1)
+ return 1;
+ atexit(UnloadResources);
+ if(Init() == -1)
+ return 1;
+
+ //Main game loop
+ userTimer = SDL_AddTimer(400, SendUserEventTick, NULL);
+ while (!exiting)
+ {
+ ProcessEvents();
+ if (Update() == -1)
+ return 1;
+ Draw();
+ }
+ Save();
+ return 0;
+}
\ No newline at end of file
diff --git a/Flow/mainOldstruct.txt b/Flow/mainOldstruct.txt
new file mode 100644
index 0000000..ec0b592
--- /dev/null
+++ b/Flow/mainOldstruct.txt
@@ -0,0 +1,2106 @@
+/*
+TODO:
+-free memory at exit
+-make more default levels
+-simplifying and shortening code
+-document code
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+typedef enum GameState
+{
+ MainMenu,
+ LevelSelectMenu,
+ TimeTrialMenu,
+ UserLevelLoading,
+ AboutMenu,
+ ActiveGame,
+ GameOver,
+ Exit
+} GameState;
+
+typedef struct Vector
+{
+ int x, y;
+} Vector;
+
+typedef enum FlowElementState
+{
+ End, Inner
+} FlowElementState;
+
+typedef enum FlowElementShape
+{
+ None = 0,
+ UpS = (1<<0),
+ RightS = (1<<1),
+ DownS = (1<<2),
+ LeftS = (1<<3),
+ EndS = (1<<4)
+} FlowElementShape;
+
+typedef struct FlowElement
+{
+ struct FlowElement *prev, *next;
+ Vector position;
+ FlowElementState type;
+ FlowElementShape shape;
+} FlowElement;
+
+typedef struct Flow
+{
+ struct Flow *next;
+ FlowElement *flowElementRoot;
+ SDL_Color color;
+ int completed;
+} Flow;
+
+typedef enum LevelState
+{
+ Uncompleted, Completed, Starred
+} LevelState;
+
+typedef struct Level
+{
+ Flow *flowRoot;
+ LevelState state;
+ unsigned int timeRecord;
+ int size, flowCount, completedFlowCount, name;
+} Level;
+
+typedef struct MenuItem
+{
+ char *name, mouseDown, mouseOver;
+ GameState gameState;
+ SDL_Color color;
+} MenuItem;
+
+typedef struct TimeTrialMenuItem
+{
+ char *name, mouseDown, mouseOver;
+ int index, time;
+ SDL_Color color;
+} TimeTrialMenuItem;
+
+typedef struct Button
+{
+ Vector position;
+ GameState gameState;
+ SDL_Surface *picture;
+} Button;
+
+typedef struct LevelTile
+{
+ Vector position;
+ char mouseDown;
+} LevelTile;
+
+typedef enum MouseButtonState
+{
+ Up, Down, JustUp, JustDown
+} MouseButtonState;
+
+const int
+ FONTSIZE_BIG = 130,
+ FONTSIZE_NORMAL = 50,
+ FONTSIZE_SMALL = 13,
+ MAINMENU_ITEM_MARGIN = 200,
+ LEVEL_TILE_MARGIN_TOP = 150,
+ LEVEL_TILE_PADDING = 5,
+ LEVEL_TILE_SIZE = 130,
+ GAME_AREA_SIZE = 400,
+ GAME_AREA_GRID_WIDTH = 3,
+ FLOW_WIDTH_PERCENT = 27,
+ FLOW_END_WITDH_PERCENT = 54,
+ FLOW_BG_OPACITY = 7,
+ LEVEL_CHANGE_ARROW_DIST = 60,
+ TIME_TRIAL_MARGIN = 50,
+ GAME_OVER_AREA_WIDTH = 250;
+const SDL_Color
+ LEVELTILE_COLOR = {96, 255, 47, 0},
+ GAME_AREA_GRID_COLOR = {0, 15, 0, 0},
+ FLOWCOLORS[] = {
+ 255, 0, 0, 0,
+ 255, 255, 0, 0, //yellow
+ 0, 255, 0, 0,
+ 0, 0, 255, 0,
+ 0, 255, 255, 0, //cyan
+ 255, 124, 0, 0, //orange
+ 40, 150, 90, 0,
+};
+FPSmanager fpsM;
+SDL_Surface *screen, *leftArrowPic, *leftArrowSelectedPic, *rightArrowPic,
+ *rightArrowSelectedPic, *starPic, *cMarkPic, *menuPic, *menuSelectedPic,
+ *reloadPic, *reloadSelectedPic;
+TTF_Font *fontTitle, *fontNormal, *fontSmall;
+GameState gameState;
+Level *levels, *defaultLevels, *userLevels;
+int currentLevelIndex, currentLevelCount, defaultLevelCount, userLevelCount,
+ currentLevelSelectPage, levelSelectPageCount, innerFlowElementCount,
+ currentTimeTTime, currentTimeTScore, timeTHighScores[3],
+ timeTScoreIndex;
+char exiting, screenBlurred, loadUserLevel, KeysDown[SDLK_LAST+1] = {0},
+ *userLevelError = NULL, isTimeTrialGame;
+long currentTime;
+Uint32 aboutAnimation;
+MouseButtonState LMB;
+Vector mousePositionDown, t = {0, 0}; //TODO: felenged�skor is j� legyen
+MenuItem mainMenuItems[] = {
+ "arcade", 0, 0, LevelSelectMenu, {250,34,49},
+ "time trial", 0, 0, TimeTrialMenu, {255,191,31},
+ "user level", 0, 0, UserLevelLoading, {191,255,50},
+ "about", 0, 0, AboutMenu, {171,227,25},
+ "exit", 0, 0, Exit, {0, 196, 129}};
+Button arrowBack = {12, 33, MainMenu, NULL},
+ arrowNext = {0, 300, ActiveGame, NULL},
+ arrowPrev = {0, 300, LevelSelectMenu, NULL},
+ reload = {0, 300, ActiveGame, NULL},
+ menuButton = {0, 300, LevelSelectMenu, NULL};
+TimeTrialMenuItem timeTrialMenuItems[] = {
+ "30 sec", 0, 0, 0, 30, {250,34,49},
+ "60 sec", 0, 0, 1, 60, {255,191,31},
+ "90 sec", 0, 0, 2, 90, {191,255,50}};
+LevelTile levelTiles[9] = {0};
+FlowElement *flowElementStart;
+
+__inline int Max(int a, int b) {
+ return a > b ? a : b;
+}
+
+__inline int Min(int a, int b) {
+ return a < b ? a : b;
+}
+
+/// rentF
+/// Clears the surface to a specific color.
+///
+/// The surface.
+/// The color.
+void ClearSurface(SDL_Surface *surface, Uint32 color)
+{
+ Uint8 *pixels;
+ int i;
+ if (SDL_LockSurface(surface) == 0)
+ {
+ pixels = (Uint8 *)surface->pixels;
+ for (i=0; i < surface->h; i++)
+ {
+ memset(pixels, color, surface->w*surface->format->BytesPerPixel);
+ pixels += surface->pitch;
+ }
+ SDL_UnlockSurface(surface);
+ }
+}
+
+void PrintString(SDL_Surface *surface, SDL_Rect rect, TTF_Font *font, char *text, SDL_Color fg, SDL_Color bg)
+{
+ SDL_Surface *fontSurface = TTF_RenderText_Shaded(font, text, fg, bg);
+ if(fontSurface)
+ {
+ SDL_BlitSurface(fontSurface, 0, screen, &rect);
+ SDL_FreeSurface(fontSurface);
+ }
+}
+
+SDL_Color Darken(SDL_Color color, double amount)
+{
+ SDL_Color ret;
+ ret.r = (int) Max(0, color.r - 255 * amount);
+ ret.g = (int) Max(0, color.g - 255 * amount);
+ ret.b = (int) Max(0, color.b - 255 * amount);
+ return ret;
+}
+
+Uint32 SDLColorTo32bit(SDL_Color color)
+{
+ Uint32 ret, t;
+ t = color.r;
+ ret = t << 24;
+ t = color.g;
+ ret |= t << 16;
+ t = color.b;
+ ret |= t << 8;
+ return ret |= 0xff;
+}
+
+Uint32 SetOpacity(Uint32 color, int percent)
+{
+ return ((color>>8)<<8) | (Uint32)((double)0xff*(double)percent/100);
+}
+
+Uint32 GetPixel(SDL_Surface *surface, int x, int y)
+{
+ int bpp = surface->format->BytesPerPixel;
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
+ switch (bpp)
+ {
+ case 1:
+ return *p;
+ case 2:
+ return *(Uint16 *)p;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
+ return p[0] << 16 | p[1] << 8 | p[2];
+ else
+ return p[0] | p[1] << 8 | p[2] << 16;
+ case 4:
+ return *(Uint32 *)p;
+ default:
+ return 0;
+ }
+}
+
+void SetPixel(SDL_Surface *surface, int x, int y, Uint32 pixel)
+{
+ int bpp = surface->format->BytesPerPixel;
+ Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
+ switch (bpp)
+ {
+ case 1:
+ *p = pixel;
+ break;
+ case 2:
+ *(Uint16 *)p = pixel;
+ break;
+ case 3:
+ if (SDL_BYTEORDER == SDL_BIG_ENDIAN) {
+ p[0] = (pixel >> 16) & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = pixel & 0xff;
+ }
+ else {
+ p[0] = pixel & 0xff;
+ p[1] = (pixel >> 8) & 0xff;
+ p[2] = (pixel >> 16) & 0xff;
+ }
+ break;
+ case 4:
+ *(Uint32 *)p = pixel;
+ break;
+ default:
+ break;
+ }
+}
+
+SDL_Surface *FlipH(SDL_Surface *surface)
+{
+ SDL_Surface *ret = NULL;
+ int x, y, rx, ry;
+ if(SDL_MUSTLOCK(surface))
+ SDL_LockSurface(surface);
+ ret = SDL_CreateRGBSurface(SDL_SWSURFACE, surface->w, surface->h,
+ surface->format->BitsPerPixel, surface->format->Rmask,
+ surface->format->Gmask, surface->format->Bmask, surface->format->Amask);
+ for(x = 0, rx = ret->w - 1; x < ret->w; x++, rx--)
+ for(y = 0, ry = ret->h - 1; y < ret->h; y++, ry--)
+ SetPixel(ret, rx, y, GetPixel(surface, x, y));
+ if(SDL_MUSTLOCK(surface))
+ SDL_UnlockSurface(surface);
+ return ret;
+}
+
+void BlurH(SDL_Surface *surface, int amount)
+{
+ int x, y, hits, oldPixel, newPixel, bpp;
+ Uint32 color, *newColors, a[3];
+ bpp = surface->format->BytesPerPixel;
+ newColors = (Uint32 *)malloc(surface->w * sizeof(Uint32 *));
+ for (y = 0; y < surface->h; y++)
+ {
+ hits = 0;
+ a[0] = a[1] = a[2] = 0;
+ for (x = -amount; x < surface->w; x++)
+ {
+ oldPixel = x - amount - 1;
+ if (oldPixel >= 0)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + oldPixel * 4); //bpp
+ if (color != 0)
+ {
+ a[0] -= (color & 0xff0000) >> 16;
+ a[1] -= (color & 0xff00) >> 8;
+ a[2] -= (color & 0xff);
+ }
+ hits--;
+ }
+ newPixel = x + amount;
+ if (newPixel < surface->w)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + newPixel * 4);
+ if (color != 0)
+ {
+ a[0] += (color & 0xff0000) >> 16;
+ a[1] += (color & 0xff00) >> 8;
+ a[2] += (color & 0xff);
+ }
+ hits++;
+ }
+ if (x >= 0)
+ {
+ newColors[x] = (((int)((double)a[0] / hits)) << 16)
+ | (((int)((double)a[1] / hits)) << 8)
+ | (int)((double)a[2] / hits);
+ }
+ }
+ for (x = 0; x < surface->w; x++)
+ *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + x * 4) = newColors[x];
+ }
+ free(newColors);
+}
+
+void BlurW(SDL_Surface *surface, int amount)
+{
+ int x, y, hits, oldPixel, newPixel;
+ Uint32 color, *newColors, a[3];
+ newColors = (Uint32 *)malloc(surface->h * sizeof(Uint32 *));
+ for (x = 0; x < surface->w; x++)
+ {
+ hits = 0;
+ a[0] = a[1] = a[2] = 0;
+ for (y = -amount; y < surface->h; y++)
+ {
+ oldPixel = y - amount - 1;
+ if (oldPixel >= 0)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + oldPixel * surface->pitch + x * 4);
+ if (color != 0)
+ {
+ a[0] -= (color & 0xff0000) >> 16;
+ a[1] -= (color & 0xff00) >> 8;
+ a[2] -= (color & 0xff);
+ }
+ hits--;
+ }
+ newPixel = y + amount;
+ if (newPixel < surface->h)
+ {
+ color = *(Uint32*)((Uint8 *)surface->pixels + newPixel * surface->pitch + x * 4);
+ if (color != 0)
+ {
+ a[0] += (color & 0xff0000) >> 16;
+ a[1] += (color & 0xff00) >> 8;
+ a[2] += (color & 0xff);
+ }
+ hits++;
+ }
+ if (y >= 0)
+ {
+ newColors[y] = (((int)((double)a[0] / hits)) << 16)
+ | (((int)((double)a[1] / hits)) << 8)
+ | (int)((double)a[2] / hits);
+ }
+ }
+ for (y = 0; y < surface->h; y++)
+ *(Uint32*)((Uint8 *)surface->pixels + y * surface->pitch + x * 4) = newColors[y];
+ }
+ free(newColors);
+}
+
+Level *LoadLevels(FILE *file, int *count)
+{
+ char c;
+ int i = 0, flowColor, j, tombMeret = 0;
+ Flow *flow;
+ Level *levels = NULL, *levelTemp;
+ while (1)
+ {
+ if(i == tombMeret)
+ {
+ levelTemp = (Level *)malloc(sizeof(Level)*(tombMeret+1));
+ for (j = 0; j < tombMeret; j++)
+ {
+ levelTemp[j] = levels[j];
+ }
+ if (levels)
+ free(levels);
+ levels = levelTemp;
+ levels[i].flowRoot = NULL;
+ levels[i].completedFlowCount = 0;
+ tombMeret++;
+ }
+ if (levels[i].flowRoot == NULL)
+ {
+ for(c = fgetc(file); c != EOF && c != '{'; c = fgetc(file));
+ if (c == EOF)
+ return NULL;
+ levels[i].flowRoot = (Flow *)malloc(sizeof(Flow));
+ if (levels[i].flowRoot == NULL)
+ return NULL;
+ levels[i].flowCount = 0;
+ levels[i].name = i+1;
+ flow = levels[i].flowRoot;
+ flow->completed = 0;
+ flowColor = 0;
+ flow->color = FLOWCOLORS[flowColor];
+ }
+
+ levels[i].flowCount++;
+ flow->next = NULL;
+ flow->flowElementRoot = (FlowElement *)malloc(sizeof(FlowElement));
+ if(flow->flowElementRoot == NULL)
+ return NULL;
+ if(fscanf(file, "{%d,", &(flow->flowElementRoot->position.x)) < 1)
+ return NULL;
+ if(fscanf(file, "%d,", &(flow->flowElementRoot->position.y)) < 1)
+ return NULL;
+
+ flow->flowElementRoot->type = End;
+ flow->flowElementRoot->next = NULL;
+ flow->flowElementRoot->prev = (FlowElement *)malloc(sizeof(FlowElement));
+ if (flow->flowElementRoot->prev == NULL)
+ return NULL;
+ flow->flowElementRoot->prev->prev = NULL;
+ flow->flowElementRoot->prev->next = flow->flowElementRoot;
+ if(fscanf(file, "%d,", &(flow->flowElementRoot->prev->position.x)) < 1)
+ return NULL;
+ if(fscanf(file, "%d},", &(flow->flowElementRoot->prev->position.y)) < 1)
+ return NULL;
+ flow->flowElementRoot->position.x--;
+ flow->flowElementRoot->position.y--;
+ flow->flowElementRoot->prev->position.x--;
+ flow->flowElementRoot->prev->position.y--;
+ flow->flowElementRoot->prev->type = End;
+ flow->color = FLOWCOLORS[flowColor++];
+ if(fscanf(file, "%d,", &(levels[i].size)) < 1)
+ {
+ flow->next = (Flow *)malloc(sizeof(Flow));
+ if (flow->next == NULL)
+ return NULL;
+ flow = flow->next;
+ continue;
+ }
+ if(fscanf(file, "%d,", &(levels[i].state)) < 1)
+ return NULL;
+ if(fscanf(file, "%d}", &(levels[i].timeRecord)) < 1)
+ return NULL;
+ if ((c = fgetc(file)) == EOF)
+ {
+ *count = tombMeret;
+ return levels;
+ }
+ else
+ i++;
+ }
+}
+
+int RemoveFlowElement(int xIn, int yIn, char removeThis)
+{
+ Flow *f;
+ FlowElement *fElem1, *fElem2;
+ char circular, startsAtFirst;
+ int a, b;
+ for (f = levels[currentLevelIndex].flowRoot; f != NULL; f = f->next)
+ {
+ startsAtFirst = f->flowElementRoot->next != NULL ? 1 : 0;
+ circular = 0;
+ for (fElem1 = f->flowElementRoot; fElem1->prev != NULL; fElem1 = fElem1->prev)
+ {
+ if (fElem1->prev == f->flowElementRoot)
+ {
+ fElem1 = f->flowElementRoot;
+ circular = 1;
+ break;
+ }
+ }
+ fElem2 = fElem1;
+ for (; fElem1 != NULL; fElem1 = fElem1->next) //iterate through all FlowElement
+ {
+ if (fElem1->position.x == xIn && fElem1->position.y == yIn)
+ {
+ if (fElem1->type == Inner)
+ {
+ if (circular)
+ {
+ //remove from xy until end (remove the shorter part)
+ for (fElem2 = fElem1->next, a = 0; fElem2->type != End; fElem2 = fElem2->next, a++);
+ for (fElem2 = fElem1->prev, b = 0; fElem2->type != End; fElem2 = fElem2->prev, b++);
+ if (!removeThis)
+ {
+ if (a > b)
+ fElem1 = fElem1->prev;
+ else
+ fElem1 = fElem1->next;
+ }
+ if (a > b)
+ {
+ fElem1->next->prev = NULL;
+ fElem1->next = NULL;
+ }
+ else
+ {
+ fElem1->prev->next = NULL;
+ fElem1->prev = NULL;
+ }
+ for (fElem1 = (a > b ? fElem1->prev : fElem1->next);
+ (a > b ? fElem1->next : fElem1->prev)->type != End;
+ fElem1 = (a > b ? fElem1->prev : fElem1->next))
+ {
+ free(a>b?fElem1->next:fElem1->prev);
+ if (a>b)
+ fElem1->next = NULL;
+ else
+ fElem1->prev = NULL;
+ innerFlowElementCount--;
+ }
+ levels[currentLevelIndex].completedFlowCount--;
+ return 1;
+ }
+ else
+ {
+ //remove all after xy
+ if (startsAtFirst)
+ fElem1->prev->next = NULL;
+ else
+ fElem1->next->prev = NULL;
+ do
+ {
+ fElem2 = startsAtFirst ? fElem1->next : fElem1->prev;
+ free(fElem1);
+ fElem1 = fElem2;
+ innerFlowElementCount--;
+ }
+ while (fElem1);
+ return 1;
+ }
+ }
+ else
+ return 0; //End at xy
+ }
+ if (fElem1->next == fElem2) //has gone around
+ break;
+ }
+ }
+ return 1;
+}
+
+int MakeRoute(int x, int y)
+{
+ int i, j, circular;
+ FlowElement *fe1, *fe2;
+
+ if (flowElementStart == NULL)
+ return 0;
+ //if circular list the flow is already completed
+ circular = 0;
+ for (fe1 = flowElementStart; fe1->next != NULL; fe1 = fe1->next)
+ {
+ if (fe1->next == flowElementStart)
+ {
+ circular = 1;
+ break;
+ }
+ }
+ if (circular)
+ {
+ if (flowElementStart->position.x == x && flowElementStart->position.y == y)
+ {
+ if (flowElementStart->type == End) //remove all inner flowelement
+ {
+ if (flowElementStart->next->type == End)
+ {
+ RemoveFlowElement(flowElementStart->next->next->position.x,
+ flowElementStart->next->next->position.y, 1);
+ RemoveFlowElement(flowElementStart->prev->position.x,
+ flowElementStart->prev->position.y, 1);
+ }
+ else
+ {
+ RemoveFlowElement(flowElementStart->prev->prev->position.x,
+ flowElementStart->prev->prev->position.y, 1);
+ RemoveFlowElement(flowElementStart->next->position.x,
+ flowElementStart->next->position.y, 1);
+ }
+ }
+ else
+ RemoveFlowElement(flowElementStart->position.x, flowElementStart->position.y, 0);
+ }
+ else //completed flow, but not released mouse yet
+ {
+ //find flowelement under mouse if there is
+ for (fe1 = flowElementStart; fe1->next != flowElementStart &&
+ (fe1->position.x != x || fe1->position.y != y); fe1 = fe1->next){}
+ if (fe1->next != flowElementStart && fe1->type != End)
+ {
+ if (fe1->next->type == End)
+ {
+ fe1->next->prev = NULL;
+ fe1->next = NULL;
+ levels[currentLevelIndex].completedFlowCount--;
+ }
+ else if (fe1->prev->type == End)
+ {
+ fe1->prev->next = NULL;
+ fe1->prev = NULL;
+ levels[currentLevelIndex].completedFlowCount--;
+ }
+ }
+ }
+ }
+ else //not circular
+ {
+ for (fe1 = flowElementStart; fe1->next != NULL; fe1 = fe1->next)
+ if (fe1->next->type == End)
+ break;
+ //starts from the first
+ if (fe1->next == NULL)
+ {
+ printf("dsd");
+ //remove following FLowElements
+ //while not reach End or the mouse pos
+ for(; fe1->type != End && (fe1->position.x != x || fe1->position.y != y); fe1 = fe1->prev);
+ //reached the end -> xy are not in the flow
+ //remove flowElements after xy
+ if (fe1->type != End || (fe1->position.x == x && fe1->position.y == y))
+ {
+ fe2 = fe1;
+ for(; fe1->next != NULL; fe1 = fe1->next);
+ for(fe1 = fe1->prev; fe1->next != fe2; fe1 = fe1->prev)
+ {
+ if (fe1->next == flowElementStart)
+ flowElementStart = fe2;
+ free(fe1->next);
+ fe1->next = NULL;
+ innerFlowElementCount--;
+ }
+ }
+ //add new FLowElements
+ if (flowElementStart->position.x != x || flowElementStart->position.y != y)
+ {
+ for(fe1 = flowElementStart; fe1-> next != NULL; fe1 = fe1->next);
+ //vertical line
+ if (fe1->position.x == x)
+ {
+ j = fe1->position.y < y ? 1 : -1;
+ for (i = fe1->position.y + j; i-j != y; i += j)
+ {
+ if(RemoveFlowElement(x, i, 1))
+ {
+ fe1->next = (FlowElement *)malloc(sizeof(FlowElement));
+ if (fe1->next == NULL)
+ return -1;
+ fe1->next->next = NULL;
+ fe1->next->prev = fe1;
+ fe1->next->type = Inner;
+ fe1->next->position.x = x;
+ fe1->next->position.y = i;
+ fe1 = fe1->next;
+ innerFlowElementCount++;
+ }
+ else
+ {
+ for(fe2 = fe1->prev; fe2->next->type != End; fe2 = fe2->prev);
+ //flow completed
+ if(fe2->position.x == x && fe2->position.y == i)
+ {
+ fe1->next = fe2;
+ fe2->prev = fe1;
+ levels[currentLevelIndex].completedFlowCount++;
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ //horizontal line
+ else if (fe1->position.y == y)
+ {
+ j = fe1->position.x < x ? 1 : -1;
+ for (i = fe1->position.x + j; i-j != x; i += j)
+ {
+ if(RemoveFlowElement(i, y, 1))
+ {
+ fe1->next = (FlowElement *)malloc(sizeof(FlowElement));
+ if (fe1->next == NULL)
+ return -1;
+ fe1->next->next = NULL;
+ fe1->next->prev = fe1;
+ fe1->next->type = Inner;
+ fe1->next->position.x = i;
+ fe1->next->position.y = y;
+ fe1 = fe1->next;
+ innerFlowElementCount++;
+ }
+ else
+ {
+ for(fe2 = fe1->prev; fe2->next->type != End; fe2 = fe2->prev);
+ //Flow completed
+ if(fe2->position.x == i && fe2->position.y == y)
+ {
+ fe1->next = fe2;
+ fe2->prev = fe1;
+ levels[currentLevelIndex].completedFlowCount++;
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+ //remove FLowElements from the other end of the Flow
+ for(fe1 = flowElementStart; fe1->prev != NULL; fe1 = fe1->prev);
+ for(fe1 = fe1->next; fe1->prev->type != End; fe1 = fe1->next)
+ {
+ free(fe1->prev);
+ fe1->prev = NULL;
+ innerFlowElementCount--;
+ }
+ }
+ //starts from the last
+ else if (fe1->next->type == End)
+ {
+ //remove following FLowElements
+ //while not reached End or the mouse pos
+ for(fe1 = flowElementStart; fe1->prev != NULL; fe1 = fe1->prev);
+ for(; fe1->type != End && (fe1->position.x != x || fe1->position.y != y); fe1 = fe1->next);
+ //reached the end -> xy not on the flow
+ if (fe1->type != End || (fe1->position.x == x && fe1->position.y == y))
+ {
+ fe2 = fe1;
+ for(; fe1->prev != NULL; fe1 = fe1->prev);
+ for(fe1 = fe1->next; fe1->prev != fe2; fe1 = fe1->next)
+ {
+ if (fe1->prev== flowElementStart)
+ flowElementStart = fe2;
+ free(fe1->prev);
+ fe1->prev = NULL;
+ innerFlowElementCount--;
+ }
+ }
+ //add new FLowElements
+ if (flowElementStart->position.x != x || flowElementStart->position.y != y)
+ {
+ for(fe1 = flowElementStart; fe1->prev != NULL; fe1 = fe1->prev);
+ //vertical line
+ if (fe1->position.x == x)
+ {
+ j = fe1->position.y < y ? 1 : -1;
+ for (i = fe1->position.y + j; i-j != y; i += j)
+ {
+ if(RemoveFlowElement(x, i, 1))
+ {
+ fe1->prev = (FlowElement *)malloc(sizeof(FlowElement));
+ if (fe1->prev == NULL)
+ return -1;
+ fe1->prev->prev = NULL;
+ fe1->prev->next = fe1;
+ fe1->prev->type = Inner;
+ fe1->prev->position.x = x;
+ fe1->prev->position.y = i;
+ fe1 = fe1->prev;
+ innerFlowElementCount++;
+ }
+ else
+ {
+ for(fe2 = fe1->next; fe2->prev->type != End; fe2 = fe2->next);
+ //flow completed
+ if(fe2->position.x == x && fe2->position.y == i)
+ {
+ fe1->prev = fe2;
+ fe2->next = fe1;
+ levels[currentLevelIndex].completedFlowCount++;
+ return 0;
+ }
+ break;
+ }
+ }
+
+ }
+ //horizontal line
+ else if (fe1->position.y == y)
+ {
+ j = fe1->position.x < x ? 1 : -1;
+ for (i = fe1->position.x + j; i-j != x; i += j)
+ {
+ if(RemoveFlowElement(i, y, 1))
+ {
+ fe1->prev = (FlowElement *)malloc(sizeof(FlowElement));
+ if (fe1->prev == NULL)
+ return -1;
+ fe1->prev->prev = NULL;
+ fe1->prev->next = fe1;
+ fe1->prev->type = Inner;
+ fe1->prev->position.x = i;
+ fe1->prev->position.y = y;
+ fe1 = fe1->prev;
+ innerFlowElementCount++;
+ }
+ else
+ {
+ for(fe2 = fe1->next; fe2->prev->type != End; fe2 = fe2->next);
+ //flow completed
+ if(fe2->position.x == i && fe2->position.y == y)
+ {
+ fe1->prev = fe2;
+ fe2->next = fe1;
+ levels[currentLevelIndex].completedFlowCount++;
+ return 0;
+ }
+ break;
+ }
+ }
+ }
+ }
+ //remove FLowElements from the other end of the Flow
+ for(fe1 = flowElementStart; fe1->next != NULL; fe1 = fe1->next);
+ for(fe1 = fe1->prev; fe1->next->type != End; fe1 = fe1->prev)
+ {
+ free(fe1->next);
+ fe1->next = NULL;
+ innerFlowElementCount--;
+ }
+ }
+ }
+ return 0;
+}
+
+int InRect(Vector levelTemp, int w, int h, Vector b)
+{
+ return b.x >= levelTemp.x && b.x < levelTemp.x + w && b.y >= levelTemp.y && b.y < levelTemp.y + h;
+}
+
+void MakeConnection(FlowElement *from, FlowElement *to)
+{
+ if (from->position.x > to->position.x)
+ {
+ to->shape = (FlowElementShape)(to->shape | RightS);
+ }
+ else if(from->position.x < to->position.x)
+ {
+ to->shape = (FlowElementShape)(to->shape | LeftS);
+ }
+ else if (from->position.y > to->position.y)
+ {
+ to->shape = (FlowElementShape)(to->shape | DownS);
+ }
+ else
+ {
+ to->shape = (FlowElementShape)(to->shape | UpS);
+ }
+}
+
+///
+/// Sets the FlowElement shapes to visulize connnections
+///
+void SetShapes()
+{
+ Flow * f;
+ FlowElement *feA, *feB;
+ for (f = levels[currentLevelIndex].flowRoot; f != NULL; f = f->next)
+ {
+ for (feA = f->flowElementRoot; feA->prev != NULL && feA->prev != f->flowElementRoot; feA = feA->prev);
+ feB = feA;
+ for (; feA != NULL; feA = feA->next)
+ {
+ feA->shape = feA->type == End ? EndS : None;
+ if (feA->next != NULL)
+ {
+ if(feA->next->type != End || feA->type == Inner)
+ MakeConnection(feA->next, feA);
+ }
+ if (feA->prev != NULL)
+ {
+ if(feA->prev->type != End || feA->type == Inner)
+ MakeConnection(feA->prev, feA);
+ }
+ if (feA == feB->prev)
+ break; //circular
+ }
+ }
+}
+
+void RemoveAllFlowElements(Flow *f)
+{
+ FlowElement *feA;
+ int circular = 0;
+ for (feA = f->flowElementRoot; feA->next != NULL; feA = feA->next)
+ {
+ if (feA->next == f->flowElementRoot)
+ {
+ circular = 1;
+ break;
+ }
+ }
+ if (circular)
+ {
+ for (feA = f->flowElementRoot->next->next; feA->prev->type != End; feA = feA->next)
+ {
+ free(feA->prev);
+ feA->prev = NULL;
+ }
+ f->flowElementRoot->next = NULL;
+ }
+ else
+ {
+ if (f->flowElementRoot->next != NULL)
+ {
+ for(; feA->next != NULL; feA = feA->next);
+ for(feA = feA->prev; feA->next->type != End; feA = feA->prev)
+ {
+ free(feA->next);
+ feA->next = NULL;
+ }
+ }
+ else
+ {
+ for(; feA->prev != NULL; feA = feA->prev);
+ for(feA = feA->next; feA->prev->type != End; feA = feA->next)
+ {
+ free(feA->prev);
+ feA->prev = NULL;
+ }
+ }
+ }
+}
+
+void SetCurrentLevel(int levelIndex)
+{
+ Flow *f;
+ if (currentLevelCount > 1)
+ {
+ if (levelIndex > currentLevelCount - 1)
+ levelIndex = currentLevelCount - 1;
+ else if (levelIndex < 0)
+ levelIndex = 0;
+ }
+ else
+ return;
+ currentLevelIndex = levelIndex;
+ innerFlowElementCount = 0;
+ levels[currentLevelIndex].completedFlowCount = 0;
+ //reset level
+ for (f = levels[levelIndex].flowRoot; f != NULL; f = f->next)
+ {
+ RemoveAllFlowElements(f);
+ }
+ SetShapes();
+}
+
+void ProcessEvents()
+{
+ SDL_Event event;
+ while (SDL_PollEvent(&event))
+ {
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ exiting = 1;
+ break;
+ case SDL_KEYDOWN:
+ KeysDown[event.key.keysym.sym] = 1;
+ break;
+ case SDL_KEYUP:
+ KeysDown[event.key.keysym.sym] = 0;
+ break;
+ case SDL_MOUSEMOTION:
+ mousePositionDown.x = event.motion.x;
+ mousePositionDown.y = event.motion.y;
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ if (event.button.button == 1)
+ LMB = JustDown;
+ break;
+ case SDL_MOUSEBUTTONUP:
+ if (event.button.button == 1)
+ LMB = JustUp;
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Draw();
+
+int Update()
+{
+ int i, j, textW, textH, margin;
+ Vector v;
+ Flow *f1, *f2;
+ FlowElement *fElem1, *fEleB;
+ FILE *file = NULL;
+ switch (gameState)
+ {
+#pragma region zart2
+ case MainMenu:
+ v.y = MAINMENU_ITEM_MARGIN;
+ for (i = 0; i < sizeof(mainMenuItems)/sizeof(MenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, mainMenuItems[i].name, &textW, &textH);
+ v.x = (screen->w - textW) / 2;
+ if (InRect(v, textW, textH, mousePositionDown))
+ {
+ mainMenuItems[i].mouseOver = 1;
+ if(LMB == JustDown)
+ mainMenuItems[i].mouseDown = 1;
+ else if(LMB == JustUp && mainMenuItems[i].mouseDown)
+ {
+ gameState = mainMenuItems[i].gameState;
+ mainMenuItems[i].mouseDown = 0;
+ Update();
+ }
+ }
+ else
+ {
+ mainMenuItems[i].mouseOver = 0;
+ mainMenuItems[i].mouseDown = 0;
+ }
+ v.y += textH;
+ }
+ break;
+
+ case LevelSelectMenu:
+ //Back button
+ levels = defaultLevels;
+ currentLevelCount = defaultLevelCount;
+ arrowBack.gameState = MainMenu;
+ arrowNext.position.y = LEVEL_TILE_MARGIN_TOP + 3*LEVEL_TILE_SIZE + 2*LEVEL_TILE_PADDING + 20;
+ arrowPrev.position.y = arrowNext.position.y;
+ menuButton.gameState = LevelSelectMenu;
+ if (InRect(arrowBack.position, leftArrowPic->w, leftArrowPic->h, mousePositionDown))
+ if(LMB == JustUp)
+ {
+ gameState = arrowBack.gameState;
+ arrowBack.picture = leftArrowPic;
+ }
+ else
+ arrowBack.picture = leftArrowSelectedPic;
+ else
+ arrowBack.picture = leftArrowPic;
+ if (InRect(arrowPrev.position, leftArrowPic->w, leftArrowPic->h, mousePositionDown) &&
+ currentLevelSelectPage > 0)
+ if(LMB == JustUp)
+ currentLevelSelectPage--;
+ else
+ arrowPrev.picture = leftArrowSelectedPic;
+ else
+ arrowPrev.picture = leftArrowPic;
+ if (InRect(arrowNext.position, rightArrowPic->w, rightArrowPic->h, mousePositionDown) &&
+ currentLevelSelectPage < levelSelectPageCount - 1)
+ if(LMB == JustUp)
+ currentLevelSelectPage++;
+ else
+ arrowNext.picture = rightArrowSelectedPic;
+ else
+ arrowNext.picture = rightArrowPic;
+ //Tiles
+ margin = (screen->w-(3*LEVEL_TILE_SIZE+LEVEL_TILE_PADDING*2))/2;
+ v.y = LEVEL_TILE_MARGIN_TOP;
+ for (i = 0; i < 3; i++)
+ {
+ v.x = margin;
+ for (j = 0; j < 3; j++)
+ {
+ if (InRect(v, LEVEL_TILE_SIZE, LEVEL_TILE_SIZE, mousePositionDown))
+ {
+ if(LMB == JustDown)
+ levelTiles[i*3+j].mouseDown = 1;
+ else
+ {
+ if(LMB == JustUp && levelTiles[i*3+j].mouseDown)
+ {
+ SetCurrentLevel(i*3+j+currentLevelSelectPage*9);
+ gameState = ActiveGame;
+ Update();
+ arrowBack.gameState = LevelSelectMenu;
+ levelTiles[i*3+j].mouseDown = 0;
+ }
+ }
+ }
+ else
+ levelTiles[i*3+j].mouseDown = 0;
+ v.x += LEVEL_TILE_SIZE+LEVEL_TILE_PADDING;
+ }
+ v.y += LEVEL_TILE_SIZE+LEVEL_TILE_PADDING;
+ }
+ break;
+
+ case ActiveGame:
+ arrowNext.position.y = LEVEL_TILE_MARGIN_TOP + 3*LEVEL_TILE_SIZE + 2*LEVEL_TILE_PADDING + 20;
+ arrowPrev.position.y = arrowNext.position.y;
+ reload.position.y = arrowNext.position.y;
+ reload.position.x = screen->w / 2 - reload.picture->w / 2;
+ //back button
+ if (InRect(arrowBack.position, leftArrowPic->w, leftArrowPic->h, mousePositionDown))
+ if(LMB == JustUp)
+ gameState = arrowBack.gameState;
+ else
+ arrowBack.picture = leftArrowSelectedPic;
+ else
+ arrowBack.picture = leftArrowPic;
+ if (!isTimeTrialGame)
+ {
+ //next level button
+ if (InRect(arrowNext.position, rightArrowPic->w, rightArrowPic->h, mousePositionDown) && currentLevelIndex != currentLevelCount-1)
+ if(LMB == JustUp)
+ {
+ SetCurrentLevel(currentLevelIndex + 1);
+ currentLevelSelectPage = (currentLevelIndex + 1) / 9;
+ }
+ else
+ arrowNext.picture = rightArrowSelectedPic;
+ else
+ arrowNext.picture = rightArrowPic;
+ //previous level button
+ if (InRect(arrowPrev.position, leftArrowPic->w, leftArrowPic->h, mousePositionDown) && currentLevelIndex > 0)
+ if(LMB == JustUp)
+ {
+ SetCurrentLevel(currentLevelIndex - 1);
+ currentLevelSelectPage = (currentLevelIndex + 1) / 9;
+ }
+ else
+ arrowPrev.picture = leftArrowSelectedPic;
+ else
+ arrowPrev.picture = leftArrowPic;
+ }
+ else
+ {
+ if (SDL_GetTicks() - 1000 > currentTime)
+ {
+ currentTime = SDL_GetTicks();
+ if (--currentTimeTTime == 0)
+ {
+ gameState = GameOver;
+ menuButton.gameState = TimeTrialMenu;
+ Update();
+ }
+ }
+ }
+ //reload level button
+ if (InRect(reload.position, reloadPic->w, reloadPic->h, mousePositionDown))
+ if(LMB == JustUp)
+ {
+ SetCurrentLevel(currentLevelIndex);
+ reload.picture = reloadPic;
+ }
+ else
+ reload.picture = reloadSelectedPic;
+ else
+ reload.picture = reloadPic;
+ margin = (screen->w - GAME_AREA_SIZE) / 2;
+ v.x = margin;
+ v.y = LEVEL_TILE_MARGIN_TOP;
+ //get clicked flow element
+ if(LMB == JustDown)
+ {
+ flowElementStart = NULL;
+ for (f1 = levels[currentLevelIndex].flowRoot; f1 != NULL; f1 = f1->next)
+ {
+ for (fElem1 = f1->flowElementRoot; fElem1->prev != NULL &&
+ fElem1->prev != f1->flowElementRoot; fElem1 = fElem1->prev);
+ fEleB = fElem1;
+ for (; fElem1 != NULL; fElem1 = fElem1->next)
+ {
+ i = GAME_AREA_SIZE / levels[currentLevelIndex].size;
+ v.x = fElem1->position.x * i + margin;
+ v.y = fElem1->position.y * i + LEVEL_TILE_MARGIN_TOP;
+ if(InRect(v, i, i, mousePositionDown))
+ {
+ flowElementStart = fElem1;
+ break;
+ }
+ if (fElem1 == fEleB->prev)
+ break;
+ }
+ }
+ }
+ else if (LMB == Down && InRect(v, GAME_AREA_SIZE, GAME_AREA_SIZE, mousePositionDown))
+ {
+ //convert mouse position to FlowElement posisiton
+ v.x = (double)(mousePositionDown.x - margin) / ((double)GAME_AREA_SIZE / levels[currentLevelIndex].size);
+ v.y = (double)(mousePositionDown.y - LEVEL_TILE_MARGIN_TOP - 1) / ((double)GAME_AREA_SIZE / levels[currentLevelIndex].size);
+ //connect FlowElements
+ if (MakeRoute(v.x, v.y) == -1)
+ return -1; //memory error
+ SetShapes();
+ //check if game is over
+ if (levels[currentLevelIndex].flowCount == levels[currentLevelIndex].completedFlowCount)
+ {
+ if (innerFlowElementCount + levels[currentLevelIndex].flowCount*2 ==
+ levels[currentLevelIndex].size * levels[currentLevelIndex].size)
+ levels[currentLevelIndex].state = Starred;
+ else if (levels[currentLevelIndex].state != Starred)
+ levels[currentLevelIndex].state = Completed;
+ LMB = Up;
+ Draw(); //draw the last connection
+ if (isTimeTrialGame)
+ {
+ currentTimeTScore++;
+ SetCurrentLevel(rand() % defaultLevelCount);
+ }
+ else
+ {
+ gameState = GameOver;
+ Update();
+ screenBlurred = 0;
+ }
+ }
+ }
+ break;
+
+ case GameOver:
+ reload.position.y = 300;
+ reload.position.x = screen->w / 2 - (int)(leftArrowPic->w * 0.5) - LEVEL_CHANGE_ARROW_DIST;
+ arrowNext.position.y = 300;
+ if (!isTimeTrialGame)
+ {
+ //reload level button
+ if (InRect(reload.position, reloadPic->w, reloadPic->h, mousePositionDown) && levels[currentLevelIndex].state != Starred)
+ if(LMB == JustUp)
+ {
+ SetCurrentLevel(currentLevelIndex);
+ gameState = reload.gameState;
+ reload.picture = reloadPic;
+ }
+ else
+ reload.picture = reloadSelectedPic;
+ else
+ reload.picture = reloadPic;
+ //next level button
+ if (InRect(arrowNext.position, rightArrowPic->w, rightArrowPic->h, mousePositionDown) && currentLevelIndex != currentLevelCount-1)
+ if(LMB == JustUp)
+ {
+ SetCurrentLevel(currentLevelIndex + 1);
+ currentLevelSelectPage = (currentLevelIndex + 1) / 9;
+ gameState = arrowNext.gameState;
+ arrowNext.picture = rightArrowPic;
+ Update();
+ }
+ else
+ arrowNext.picture = rightArrowSelectedPic;
+ else
+ arrowNext.picture = rightArrowPic;
+ }
+ else
+ {
+ if (timeTHighScores[timeTScoreIndex] < currentTimeTScore)
+ {
+ timeTHighScores[timeTScoreIndex] = currentTimeTScore;
+ }
+ }
+ //back to menu button
+ if (InRect(menuButton.position, menuPic->w, menuPic->h, mousePositionDown))
+ if(LMB == JustUp)
+ {
+ gameState = menuButton.gameState;
+ menuButton.picture = menuPic;
+ }
+ else
+ menuButton.picture = menuSelectedPic;
+ else
+ menuButton.picture = menuPic;
+ break;
+#pragma endregion zart2
+
+ case TimeTrialMenu:
+ levels = defaultLevels;
+ arrowBack.gameState = MainMenu;
+ isTimeTrialGame = 0;
+ //Back button
+ if (InRect(arrowBack.position, leftArrowPic->w, leftArrowPic->h, mousePositionDown))
+ if(LMB == JustUp)
+ gameState = arrowBack.gameState;
+ else
+ arrowBack.picture = leftArrowSelectedPic;
+ else
+ arrowBack.picture = leftArrowPic;
+ v.y = MAINMENU_ITEM_MARGIN;
+ v.x = TIME_TRIAL_MARGIN;
+ for (i = 0; i < sizeof(timeTrialMenuItems)/sizeof(TimeTrialMenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, timeTrialMenuItems[i].name, &textW, &textH);
+ if (InRect(v, textW, textH, mousePositionDown))
+ {
+ timeTrialMenuItems[i].mouseOver = 1;
+ if(LMB == JustDown)
+ timeTrialMenuItems[i].mouseDown = 1;
+ else if(LMB == JustUp && timeTrialMenuItems[i].mouseDown)
+ {
+ gameState = ActiveGame;
+ timeTrialMenuItems[i].mouseDown = 0;
+ timeTrialMenuItems[i].mouseOver = 0;
+ currentTimeTTime = timeTrialMenuItems[i].time + 1;
+ timeTScoreIndex =timeTrialMenuItems[i].index;
+ isTimeTrialGame = 1;
+ currentTimeTScore = 0;
+ SetCurrentLevel(rand() % defaultLevelCount);
+ arrowBack.gameState = TimeTrialMenu;
+ Update();
+ }
+ }
+ else
+ {
+ timeTrialMenuItems[i].mouseDown = 0;
+ timeTrialMenuItems[i].mouseOver = 0;
+ }
+ v.y += textH;
+ }
+ break;
+
+ case UserLevelLoading:
+ if (loadUserLevel)
+ {
+ loadUserLevel = 0;
+ menuButton.gameState = MainMenu;
+ arrowBack.gameState = MainMenu;
+ if ((file = fopen("userLevels.txt", "rt")) == NULL)
+ userLevelError = "\"userLevels.txt\" could not be loaded.";
+ else
+ {
+ if (userLevels != NULL) //free previously loaded levels
+ {
+ for (i = 0; i < userLevelCount; i++)
+ {
+ for (f1 = userLevels[i].flowRoot; f1 != NULL; )
+ {
+ RemoveAllFlowElements(f1);
+ free(f1->flowElementRoot->prev);
+ free(f1->flowElementRoot);
+ f2 = f1;
+ f1 = f1->next;
+ free(f2);
+ }
+ }
+ free(userLevels);
+ }
+ if ((userLevels = LoadLevels(file, &userLevelCount)) == NULL)
+ userLevelError = "\"userLevels.txt\" contains wrong format.";
+ else
+ {
+ levels = userLevels;
+ currentLevelCount = userLevelCount;
+ SetCurrentLevel(0);
+ loadUserLevel = 1;
+ gameState = ActiveGame;
+ Update();
+ }
+ fclose(file);
+ }
+ screenBlurred = 0;
+ }
+ //back to menu button
+ if (InRect(menuButton.position, menuPic->w, menuPic->h, mousePositionDown))
+ if(LMB == JustUp)
+ {
+ gameState = menuButton.gameState;
+ menuButton.picture = menuPic;
+ loadUserLevel = 1;
+ }
+ else
+ menuButton.picture = menuSelectedPic;
+ else
+ menuButton.picture = menuPic;
+ break;
+
+ case AboutMenu:
+ //Back button
+ if (InRect(arrowBack.position, leftArrowPic->w, leftArrowPic->h, mousePositionDown))
+ if(LMB == JustUp)
+ gameState = arrowBack.gameState;
+ else
+ arrowBack.picture = leftArrowSelectedPic;
+ else
+ arrowBack.picture = leftArrowPic;
+ break;
+
+ case Exit:
+ exiting = 1;
+ break;
+ }
+ switch (LMB)
+ {
+ case JustUp:
+ LMB = Up;
+ break;
+ case JustDown:
+ LMB = Down;
+ break;
+ default:
+ break;
+ }
+ if (KeysDown[SDLK_UP])
+ t.y -= 3;
+ if (KeysDown[SDLK_RIGHT])
+ t.x += 3;
+ if (KeysDown[SDLK_DOWN])
+ t.y += 3;
+ if (KeysDown[SDLK_LEFT])
+ t.x -=3;
+ if (KeysDown[SDLK_ESCAPE])
+ exiting = 1;
+ return 0;
+}
+
+void Draw()
+{
+ SDL_Color white = {255,255,255};
+ SDL_Color black = {0,0,0};
+ char str1[60], str2[60];
+ int i, j, textW, textH, margin;
+ SDL_Rect r = {0, 0, 0, 0};
+ Flow *f;
+ FlowElement *fElem1, *fElem2;
+ SDL_Surface *surface;
+ switch (gameState)
+ {
+#pragma region zart
+ case MainMenu:
+ ClearSurface(screen, SDL_MapRGB(screen->format, 0, 0, 0));
+ TTF_SizeText(fontTitle,"flow", &textW, NULL);
+ r.x = (screen->w - textW) / 2;
+ PrintString(screen, r, fontTitle, "flow", white, black);
+ r.y = MAINMENU_ITEM_MARGIN;
+ for (i = 0; i < sizeof(mainMenuItems)/sizeof(MenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, mainMenuItems[i].name, &textW, &textH);
+ r.x = (screen->w - textW) / 2;
+ if (mainMenuItems[i].mouseOver)
+ {
+ if (mainMenuItems[i].mouseDown)
+ PrintString(screen, r, fontNormal, mainMenuItems[i].name, Darken(mainMenuItems[i].color, 0.3), mainMenuItems[i].color);
+ else
+ PrintString(screen, r, fontNormal, mainMenuItems[i].name, Darken(mainMenuItems[i].color, 0.3), black);
+ }
+ else
+ PrintString(screen, r, fontNormal, mainMenuItems[i].name, mainMenuItems[i].color, black);
+ r.y += textH;
+ }
+ break;
+
+ case LevelSelectMenu:
+ ClearSurface(screen, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ if (currentLevelSelectPage > 0)
+ {
+ r.x = arrowPrev.position.x;
+ r.y = arrowPrev.position.y;
+ SDL_BlitSurface(arrowPrev.picture, 0, screen, &r);
+ }
+ if (currentLevelSelectPage < levelSelectPageCount - 1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+
+ //current/all LevelTile page
+ *str1 = 0;
+ _itoa(currentLevelSelectPage+1, str2, 10);
+ strcat(str1, str2);
+ strcat(str1, "/");
+ _itoa(levelSelectPageCount, str2, 10);
+ strcat(str1, str2);
+ TTF_SizeText(fontSmall, str1, &textW, NULL);
+ r.x = screen->w/2-textW/2;
+ r.y = LEVEL_TILE_MARGIN_TOP - 30;
+ PrintString(screen, r, fontSmall, str1, white, black);
+
+ //Tiles
+ margin = (screen->w-(3*LEVEL_TILE_SIZE+LEVEL_TILE_PADDING*2))/2;
+ r.y = LEVEL_TILE_MARGIN_TOP;
+ for (i = 0; i < 3; i++)
+ {
+ r.x = margin;
+ for (j = 0; j < 3; j++)
+ {
+ itoa(i*3+j+1+currentLevelSelectPage*9, str1, 10);
+ TTF_SizeText(fontNormal, str1, &textW, &textH);
+ if (levelTiles[i*3+j].mouseDown)
+ {
+ boxColor(screen, r.x, r.y, r.x + LEVEL_TILE_SIZE, r.y + LEVEL_TILE_SIZE,
+ SDLColorTo32bit(Darken(LEVELTILE_COLOR, 0.3)));
+ r.x += (LEVEL_TILE_SIZE-textW)/2;
+ r.y += (LEVEL_TILE_SIZE-textH)/2;
+ PrintString(screen, r, fontNormal, str1, black, Darken(LEVELTILE_COLOR, 0.3));
+ r.x -= (LEVEL_TILE_SIZE-textW)/2;
+ r.y -= (LEVEL_TILE_SIZE-textH)/2;
+ }
+ else
+ {
+ boxColor(screen, r.x, r.y, r.x + LEVEL_TILE_SIZE, r.y + LEVEL_TILE_SIZE,
+ SDLColorTo32bit(LEVELTILE_COLOR));
+ r.x += (LEVEL_TILE_SIZE-textW)/2;
+ r.y += (LEVEL_TILE_SIZE-textH)/2;
+ PrintString(screen, r, fontNormal, str1, black, LEVELTILE_COLOR);
+ r.x -= (LEVEL_TILE_SIZE-textW)/2;
+ r.y -= (LEVEL_TILE_SIZE-textH)/2;
+ }
+ if (i*3+j+currentLevelSelectPage*9 < currentLevelCount)
+ {
+ r.x += LEVEL_TILE_SIZE - cMarkPic->w-5;
+ r.y += LEVEL_TILE_SIZE - cMarkPic->h-5;
+ switch (levels[i*3+j+currentLevelSelectPage*9].state)
+ {
+ case Completed:
+ SDL_BlitSurface(cMarkPic, 0, screen, &r);
+ break;
+ case Starred:
+ SDL_BlitSurface(starPic, 0, screen, &r);
+ break;
+ default:
+ break;
+ }
+ r.y -= LEVEL_TILE_SIZE - cMarkPic->h-5;
+ r.x -= LEVEL_TILE_SIZE - cMarkPic->w-5;
+ }
+ r.x += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ r.y += LEVEL_TILE_SIZE + LEVEL_TILE_PADDING;
+ }
+ r.x = 50;
+ r.y = 0;
+ PrintString(screen, r, fontNormal, "choose level", white, black);
+ break;
+
+ case ActiveGame:
+ ClearSurface(screen, SDL_MapRGB(screen->format, 0, 0, 0));
+ //back button
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ if (!isTimeTrialGame)
+ {
+ //previous level button
+ if (currentLevelIndex > 0)
+ {
+ r.x = arrowPrev.position.x;
+ r.y = arrowPrev.position.y;
+ SDL_BlitSurface(arrowPrev.picture, 0, screen, &r);
+ }
+ //next level button
+ if (currentLevelIndex != currentLevelCount-1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+ }
+ else
+ {
+ //time left
+ *str1 = 0;
+ _itoa(currentTimeTTime, str1, 10);
+ TTF_SizeText(fontSmall, str1, &textW, &textH);
+ r.x = (screen->w + GAME_AREA_SIZE) / 2 - 20;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ PrintString(screen, r, fontSmall, str1, white, black);
+ //current score
+ *str1 = 0;
+ strcat(str1, "completed: ");
+ _itoa(currentTimeTScore, str2, 10);
+ strcat(str1, str2);
+ TTF_SizeText(fontSmall, str1, &textW, &textH);
+ r.x = (screen->w + textW) / 2;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ PrintString(screen, r, fontSmall, str1, white, black);
+ }
+ //reload level button
+ r.x = reload.position.x;
+ r.y = reload.position.y;
+ SDL_BlitSurface(reload.picture, 0, screen, &r);
+ margin = (screen->w - GAME_AREA_SIZE)/2;
+ //comleted/all flow count
+ *str1 = 0;
+ strcat(str1, "flows: ");
+ _itoa(levels[currentLevelIndex].completedFlowCount, str2, 10);
+ strcat(str1, str2);
+ strcat(str1, "/");
+ _itoa(levels[currentLevelIndex].flowCount, str2, 10);
+ strcat(str1, str2);
+ TTF_SizeText(fontSmall, str1, &textW, &textH);
+ r.x = margin;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ PrintString(screen, r, fontSmall, str1, white, black);
+ //percent
+ *str1 = 0;
+ _itoa((int)((double)(innerFlowElementCount + levels[currentLevelIndex].completedFlowCount) *
+ 100 / (levels[currentLevelIndex].size * levels[currentLevelIndex].size +
+ levels[currentLevelIndex].flowCount - levels[currentLevelIndex].flowCount*2)), str1, 10);
+ strcat(str1, "%");
+ r.x = margin + 100;
+ r.y = LEVEL_TILE_MARGIN_TOP - textH - 10;
+ PrintString(screen, r, fontSmall, str1, white, black);
+ //level state
+ r.x = screen->w - cMarkPic->w - 10;
+ r.y = 30;
+ switch (levels[currentLevelIndex].state)
+ {
+ case Completed:
+ SDL_BlitSurface(cMarkPic, 0, screen, &r);
+ break;
+ case Starred:
+ SDL_BlitSurface(starPic, 0, screen, &r);
+ break;
+ default:
+ break;
+ }
+ //horizontal grid
+ for (i = 0; i <= levels[currentLevelIndex].size; i++)
+ {
+ r.x = margin-GAME_AREA_GRID_WIDTH;
+ r.w = GAME_AREA_GRID_WIDTH + levels[currentLevelIndex].size*
+ ((GAME_AREA_SIZE-(levels[currentLevelIndex].size-1)*
+ GAME_AREA_GRID_WIDTH)/levels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH) - 1;
+ r.y = LEVEL_TILE_MARGIN_TOP-GAME_AREA_GRID_WIDTH +
+ i*((GAME_AREA_SIZE-(levels[currentLevelIndex].size-1)*
+ GAME_AREA_GRID_WIDTH)/levels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH);
+ r.h = GAME_AREA_GRID_WIDTH-1;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SDLColorTo32bit(GAME_AREA_GRID_COLOR));
+ }
+ //vertical grid
+ for (i = 0; i <= levels[currentLevelIndex].size; i++)
+ {
+ r.x = margin-GAME_AREA_GRID_WIDTH +
+ i*((GAME_AREA_SIZE-(levels[currentLevelIndex].size-1) *
+ GAME_AREA_GRID_WIDTH)/levels[currentLevelIndex].size + GAME_AREA_GRID_WIDTH);
+ r.w = GAME_AREA_GRID_WIDTH-1;
+ r.y = LEVEL_TILE_MARGIN_TOP - GAME_AREA_GRID_WIDTH;
+ r.h = GAME_AREA_SIZE + GAME_AREA_GRID_WIDTH - 1;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SDLColorTo32bit(GAME_AREA_GRID_COLOR));
+ }
+ //Flows
+ r.w = (GAME_AREA_SIZE-(levels[currentLevelIndex].size-1)*GAME_AREA_GRID_WIDTH)/levels[currentLevelIndex].size-1;
+ r.h = r.w;
+ i = r.w*FLOW_WIDTH_PERCENT/100.0; //current flow width
+ j = r.w*FLOW_END_WITDH_PERCENT/100.0; //current flow end width
+ for (f = levels[currentLevelIndex].flowRoot; f != NULL; f = f->next)
+ {
+ //FlowElements
+ for (fElem1 = f->flowElementRoot; fElem1->prev != NULL && fElem1->prev != f->flowElementRoot; fElem1 = fElem1->prev);
+ if (fElem1->prev != NULL)
+ fElem1 = fElem1->prev;
+ fElem2 = fElem1;
+ for (; fElem1 != NULL; fElem1 = fElem1->next)
+ {
+ r.x = fElem1->position.x*(r.w + GAME_AREA_GRID_WIDTH + 1) + margin;
+ r.y = fElem1->position.y*(r.w + GAME_AREA_GRID_WIDTH + 1) + LEVEL_TILE_MARGIN_TOP;
+ boxColor(screen, r.x, r.y, r.x + r.w, r.y + r.h, SetOpacity(SDLColorTo32bit(f->color), FLOW_BG_OPACITY));
+ if (fElem1->shape & UpS)
+ {
+ boxColor(screen, r.x+(r.w-i)/2, r.y, r.x+(r.w-i)/2+i,
+ r.y +(r.h-i)/2 + i, SDLColorTo32bit(f->color));
+ }
+ //overlap grid
+ if (fElem1->shape & DownS)
+ {
+ boxColor(screen, r.x+(r.w-i)/2, r.y + r.w + GAME_AREA_GRID_WIDTH,
+ r.x+(r.w-i)/2+i, r.y +(r.h-i)/2, SDLColorTo32bit(f->color));
+ }
+ //overlap grid
+ if (fElem1->shape & RightS)
+ {
+ boxColor(screen, r.x+(r.w-i)/2, r.y +(r.h-i)/2 + i, r.x+r.w +
+ GAME_AREA_GRID_WIDTH, r.y +(r.h-i)/2, SDLColorTo32bit(f->color));
+ }
+ if (fElem1->shape & LeftS)
+ {
+ boxColor(screen, r.x, r.y +(r.h-i)/2, r.x+(r.w-i)/2+i,
+ r.y +(r.h-i)/2 + i, SDLColorTo32bit(f->color));
+ }
+ if (fElem1->shape & EndS)
+ {
+ boxColor(screen, r.x+(r.w-j)/2, r.y +(r.h-j)/2, r.x+
+ (r.w-j)/2+j, r.y +(r.h-j)/2 +
+ j, SDLColorTo32bit(f->color));
+ }
+ if (fElem1 == fElem2->prev)
+ break; //circular
+ }
+ }
+ rectangleColor(screen, margin, LEVEL_TILE_MARGIN_TOP, margin + GAME_AREA_SIZE, LEVEL_TILE_MARGIN_TOP + GAME_AREA_SIZE, 0xffffffff);
+ //print level name
+ *str1 = 0;
+ strcat(str1, "level ");
+ _itoa(levels[currentLevelIndex].name, str2, 10);
+ strcat(str1, str2);
+ r.x = 50;
+ r.y = 0;
+ PrintString(screen, r, fontNormal, str1, white, black);
+ break;
+
+ case GameOver:
+ //blur previous screen
+ if (!screenBlurred)
+ {
+ BlurH(screen, 2);
+ BlurW(screen, 2);
+ screenBlurred = 1;
+ }
+ if (!isTimeTrialGame)
+ {
+ if (levels[currentLevelIndex].state != Starred)
+ {
+ TTF_SizeText(fontSmall, "fill the whole game area.", &textW, &textH);
+ r.y = arrowNext.position.y-arrowNext.picture->h-textH;
+ r.x = screen->w/2 - textW/2;
+ PrintString(screen, r, fontSmall, "fill the whole game area.", white, black);
+ r.y -= textH;
+ TTF_SizeText(fontSmall, "If you want to get a star,", &textW, &textH);
+ r.x = screen->w/2 - textW/2;
+ PrintString(screen, r, fontSmall, "If you want to get a star,", white, black);
+ TTF_SizeText(fontNormal, "completed", &textW, &textH);
+ r.x = screen->w/2 - textW/2;
+ r.y -= textH;
+ PrintString(screen, r, fontNormal, "completed", white, black);
+ //reload level button
+ r.x = reload.position.x;
+ r.y = reload.position.y;
+ SDL_BlitSurface(reload.picture, 0, screen, &r);
+ }
+ else
+ {
+ TTF_SizeText(fontNormal, "completed", &textW, &textH);
+ r.x = screen->w/2 - textW/2;
+ r.y = arrowNext.position.y-arrowNext.picture->h-textH;
+ PrintString(screen, r, fontNormal, "completed", white, black);
+ }
+ if (currentLevelIndex != currentLevelCount-1)
+ {
+ r.x = arrowNext.position.x;
+ r.y = arrowNext.position.y;
+ SDL_BlitSurface(arrowNext.picture, 0, screen, &r);
+ }
+ }
+ else
+ {
+ TTF_SizeText(fontNormal, "game over", &textW, &textH);
+ r.x = (screen->w - textW) / 2;
+ r.y = arrowNext.position.y-arrowNext.picture->h-textH;
+ PrintString(screen, r, fontNormal, "game over", white, black);
+ //score
+ *str1 = 0;
+ strcat(str1, "you have made ");
+ _itoa(currentTimeTScore, str2, 10);
+ strcat(str1, str2);
+ strcat(str1, " levels");
+ TTF_SizeText(fontSmall, str1, &textW, NULL);
+ r.x = screen->w/2 - textW/2;
+ r.y += textH;
+ PrintString(screen, r, fontSmall, str1, white, black);
+ }
+ //back to menu button
+ r.x = menuButton.position.x;
+ r.y = menuButton.position.y;
+ SDL_BlitSurface(menuButton.picture, 0, screen, &r);
+ break;
+
+ case UserLevelLoading:
+ if (!screenBlurred)
+ {
+ BlurH(screen, 2);
+ BlurW(screen, 2);
+ screenBlurred = 1;
+ }
+ TTF_SizeText(fontSmall, userLevelError, &textW, &textH);
+ r.y = arrowNext.position.y-arrowNext.picture->h-textH;
+ r.x = screen->w/2 - textW/2;
+ PrintString(screen, r, fontSmall, userLevelError, white, black);
+ r.x = menuButton.position.x;
+ r.y = menuButton.position.y;
+ SDL_BlitSurface(menuButton.picture, 0, screen, &r);
+ break;
+#pragma endregion zart
+
+ case TimeTrialMenu:
+ ClearSurface(screen, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r); r.x = 50;
+ r.y = 0;
+ PrintString(screen, r, fontNormal, "time trial", white, black);
+ r.y = MAINMENU_ITEM_MARGIN;
+ r.x = TIME_TRIAL_MARGIN;
+ for (i = 0; i < sizeof(timeTrialMenuItems)/sizeof(TimeTrialMenuItem); i++)
+ {
+ TTF_SizeText(fontNormal, timeTrialMenuItems[i].name, NULL, &textH);
+ if (timeTrialMenuItems[i].mouseOver)
+ {
+ if (timeTrialMenuItems[i].mouseDown)
+ PrintString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ Darken(timeTrialMenuItems[i].color, 0.3), timeTrialMenuItems[i].color);
+ else
+ PrintString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ Darken(timeTrialMenuItems[i].color, 0.3), black);
+ }
+ else
+ PrintString(screen, r, fontNormal, timeTrialMenuItems[i].name,
+ timeTrialMenuItems[i].color, black);
+ if (timeTHighScores[timeTrialMenuItems[i].index] != 0)
+ {
+ *str1 = 0;
+ _itoa(timeTHighScores[timeTrialMenuItems[i].index], str1, 10);
+ TTF_SizeText(fontNormal, str1, &textW, NULL);
+ r.x = screen->w - textW - TIME_TRIAL_MARGIN;
+ PrintString(screen, r, fontNormal, str1, white, black);
+ r.x = TIME_TRIAL_MARGIN;
+ }
+ r.y += textH;
+ }
+ break;
+
+ case AboutMenu:
+ ClearSurface(screen, SDL_MapRGB(screen->format, 0, 0, 0));
+ r.x = arrowBack.position.x;
+ r.y = arrowBack.position.y;
+ SDL_BlitSurface(arrowBack.picture, 0, screen, &r);
+ r.x = 50;
+ r.y = 0;
+ PrintString(screen, r, fontNormal, "about", white, black);
+ TTF_SizeText(fontNormal, "szabolevente", &textW, NULL);
+ r.x = screen->w / 2 - textW / 2;
+ r.y = 200;
+ aboutAnimation += 0xc3a3;
+ white.r = (aboutAnimation & 0xff0000) >> 16;
+ white.g = (aboutAnimation & 0xff00) >> 8;
+ white.b = (aboutAnimation & 0xff);
+ PrintString(screen, r, fontNormal, "szabolevente", white, black);
+ TTF_SizeText(fontNormal, "@mail.com", &textW, &textH);
+ r.x = screen->w / 2 - textW / 2;
+ r.y += textH;
+ PrintString(screen, r, fontNormal, "@mail.com", white, black);
+ white.r = 255;
+ white.g = 255;
+ white.b = 255;
+ break;
+ }
+ _itoa(t.x, str1, 10);
+ stringRGBA(screen, 0, 0, str1, 255, 255, 255, 255);
+ _itoa(t.y, str1, 10);
+ stringRGBA(screen, 0, 10, str1, 255, 255, 255, 255);
+ _itoa(levels[currentLevelIndex].flowCount, str1, 10);
+ stringRGBA(screen, 0, 20, str1, 255, 255, 255, 255);
+ SDL_Flip(screen);
+}
+
+int LoadResources()
+{
+ FILE *file;
+ if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0)
+ {
+ printf( "Unable to init SDL: %s\n", SDL_GetError());
+ return -1;
+ }
+ atexit(SDL_Quit);
+ if(TTF_Init()==-1)
+ {
+ printf("Unable to init TTF: %s\n", TTF_GetError());
+ return -1;
+ }
+ atexit(TTF_Quit);
+ if ((screen = SDL_SetVideoMode(480, 640, 0, SDL_HWSURFACE|SDL_DOUBLEBUF)) == NULL)
+ {
+ printf("Unable to set 480x640 video: %s\n", SDL_GetError());
+ return 1;
+ }
+ if(IMG_Init(IMG_INIT_TIF | IMG_INIT_JPG | IMG_INIT_PNG) == 0)
+ {
+ printf("Failed to init required support!\n");
+ }
+ atexit(IMG_Quit);
+ if ((fontTitle = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_BIG)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((fontNormal = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_NORMAL)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((fontSmall = TTF_OpenFont("DunkinSans.ttf", FONTSIZE_SMALL)) == NULL)
+ {
+ printf("Unable to open font.\n");
+ return -1;
+ }
+ if ((leftArrowPic = IMG_Load("left_arrow.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((leftArrowSelectedPic = IMG_Load("left_arrow_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((starPic = IMG_Load("star.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((cMarkPic = IMG_Load("cMark.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((menuPic = IMG_Load("menu.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((menuSelectedPic = IMG_Load("menu_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((reloadPic = IMG_Load("reload.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ if ((reloadSelectedPic = IMG_Load("reload_clicked.png")) == NULL)
+ {
+ printf("Unable to load bitmap: %s\n", SDL_GetError());
+ return -1;
+ }
+ //Load default levels
+ if ((file = fopen("defaultLevels.txt", "rt")) == NULL)
+ {
+ printf("Unable to load level\n");
+ return -1;
+ }
+ if ((defaultLevels = LoadLevels(file, &defaultLevelCount)) == NULL)
+ {
+ printf("Unable to parse level\n");
+ fclose(file);
+ return -1;
+ }
+ fclose(file);
+ //load time trial high scores
+ timeTHighScores[0] = 0;
+ timeTHighScores[1] = 0;
+ timeTHighScores[2] = 0;
+ if ((file = fopen("scores.txt", "rt")) != NULL)
+ {
+ fscanf(file, "%d,", &timeTHighScores[0]);
+ fscanf(file, "%d,", &timeTHighScores[1]);
+ fscanf(file, "%d", &timeTHighScores[2]);
+ fclose(file);
+ }
+ return 0;
+}
+
+void UnloadResources()
+{
+ SDL_FreeSurface(leftArrowPic);
+ SDL_FreeSurface(leftArrowSelectedPic);
+ SDL_FreeSurface(rightArrowPic);
+ SDL_FreeSurface(rightArrowSelectedPic);
+ SDL_FreeSurface(starPic);
+ SDL_FreeSurface(cMarkPic);
+ SDL_FreeSurface(menuPic);
+ SDL_FreeSurface(menuSelectedPic);
+ SDL_FreeSurface(screen);
+ TTF_CloseFont(fontTitle);
+ TTF_CloseFont(fontNormal);
+ TTF_CloseFont(fontSmall);
+}
+
+int Init()
+{
+ SDL_WM_SetCaption("Flow", "Flow");
+ SDL_initFramerate(&fpsM);
+ SDL_setFramerate(&fpsM, 30);
+ srand(SDL_GetTicks());
+
+ //Initialise game vars
+ gameState = MainMenu;
+ mousePositionDown.x = 0;
+ mousePositionDown.y = 0;
+ exiting = 0;
+ loadUserLevel = 1;
+ levels = defaultLevels;
+ userLevels = NULL;
+ currentLevelCount = defaultLevelCount;
+ currentLevelIndex = 0;
+ currentLevelSelectPage = 0;
+ levelSelectPageCount = 1 + (defaultLevelCount-1)/9;
+ aboutAnimation = 0;
+ mousePositionDown.x = 0;
+ mousePositionDown.y = 0;
+ isTimeTrialGame = 0;
+ currentTime = SDL_GetTicks();
+
+ //set button pictures
+ arrowBack.picture = leftArrowPic;
+ if ((rightArrowPic = FlipH(leftArrowPic)) == NULL)
+ {
+ printf("Unable to flip bitmap\n");
+ return -1;
+ }
+ if ((rightArrowSelectedPic = FlipH(leftArrowSelectedPic)) == NULL)
+ {
+ printf("Unable to flip bitmap\n");
+ return -1;
+ }
+ reload.picture = reloadPic;
+ arrowNext.picture = rightArrowPic;
+ arrowPrev.picture = leftArrowPic;
+ menuButton.picture = menuPic;
+
+ //set button positions
+ arrowNext.position.x = screen->w / 2 - (int)(leftArrowPic->w * 0.5) + LEVEL_CHANGE_ARROW_DIST;
+ arrowPrev.position.x = screen->w / 2 - (int)(leftArrowPic->w * 0.5) - LEVEL_CHANGE_ARROW_DIST;
+ menuButton.position.x = screen->w / 2 - menuButton.picture->w * 0.5;
+ return 0;
+}
+
+void Save()
+{
+ FILE *file;
+ Flow *f;
+ int i;
+ if ((file = fopen("defaultLevels.txt", "wt")) != NULL)
+ {
+ for (i = 0; i < defaultLevelCount; i++)
+ {
+ fprintf(file, "{");
+ for (f = defaultLevels[i].flowRoot; f != NULL; f = f->next)
+ {
+ fprintf(file, "{%d,%d,%d,%d},",
+ f->flowElementRoot->position.x + 1,
+ f->flowElementRoot->position.y + 1,
+ f->flowElementRoot->prev->position.x + 1,
+ f->flowElementRoot->prev->position.y + 1);
+ }
+ if (i == defaultLevelCount - 1)
+ fprintf(file, "%d,%d,0}", defaultLevels[i].size, defaultLevels[i].state);
+ else
+ fprintf(file, "%d,%d,0}\n", defaultLevels[i].size, defaultLevels[i].state);
+ }
+ fclose(file);
+ }
+ if ((file = fopen("scores.txt", "wt")) != NULL)
+ {
+ fprintf(file, "%d,%d,%d", timeTHighScores[0], timeTHighScores[1], timeTHighScores[2]);
+ fclose(file);
+ }
+}
+
+int main(int argc, char* argv[])
+{
+ Flow *f;
+ if(LoadResources() == -1)
+ return 1;
+ atexit(UnloadResources);
+ if(Init() == -1)
+ return 1;
+
+ //Main game loop
+ while (!exiting)
+ {
+ ProcessEvents();
+ if (Update() == -1)
+ return 1;
+ Draw();
+ SDL_framerateDelay(&fpsM);
+ }
+ Save();
+ return 0;
+}
diff --git a/Flow/menu.png b/Flow/menu.png
new file mode 100644
index 0000000..b46869e
Binary files /dev/null and b/Flow/menu.png differ
diff --git a/Flow/menu_clicked.png b/Flow/menu_clicked.png
new file mode 100644
index 0000000..46326a8
Binary files /dev/null and b/Flow/menu_clicked.png differ
diff --git a/Flow/napl.txt b/Flow/napl.txt
new file mode 100644
index 0000000..577af1e
--- /dev/null
+++ b/Flow/napl.txt
@@ -0,0 +1,43 @@
+********************************************************
+*
+* MEMORIASZIVARGAS VAN A PROGRAMBAN!!!
+*
+********************************************************
+** DEBUGMALLOC DUMP ************************************
+
+ MEMORIATERULET: 008889B8, kanari: ok
+ foglalva itt: d:\bme\prog1\elmelet\flow\flow\main.c:730
+ foglalas modja: malloc(sizeof(FlowElement)) (20 bajt)
+ memoria eleje:
+ 0000 f8 43 88 00 e8 8b 88 00 04 00 02 00 fa 6b 27 ac .C...........k'.
+ 0010 05 00 00 00 ....
+
+ MEMORIATERULET: 00888788, kanari: ok
+ foglalva itt: d:\bme\prog1\elmelet\flow\flow\main.c:782
+ foglalas modja: malloc(sizeof(FlowElement)) (20 bajt)
+ memoria eleje:
+ 0000 98 d9 87 00 58 85 88 00 03 00 00 00 83 74 af 34 ....X........t.4
+ 0010 06 00 00 00 ....
+
+ MEMORIATERULET: 00887EC8, kanari: ok
+ foglalva itt: d:\bme\prog1\elmelet\flow\flow\main.c:730
+ foglalas modja: malloc(sizeof(FlowElement)) (20 bajt)
+ memoria eleje:
+ 0000 00 3b 88 00 f8 80 88 00 02 00 02 00 cd 7b 73 f3 .;...........{s.
+ 0010 05 00 00 00 ....
+
+ MEMORIATERULET: 00887C98, kanari: ok
+ foglalva itt: d:\bme\prog1\elmelet\flow\flow\main.c:782
+ foglalas modja: malloc(sizeof(FlowElement)) (20 bajt)
+ memoria eleje:
+ 0000 70 3f 88 00 68 7a 88 00 01 00 00 00 a8 d4 4a c9 p?..hz........J.
+ 0010 06 00 00 00 ....
+
+ MEMORIATERULET: 00882DA0, kanari: ok
+ foglalva itt: d:\bme\prog1\elmelet\flow\flow\main.c:730
+ foglalas modja: malloc(sizeof(FlowElement)) (20 bajt)
+ memoria eleje:
+ 0000 c8 db 87 00 d0 2e 88 00 00 00 01 00 d0 37 27 e2 .............7'.
+ 0010 05 00 00 00 ....
+
+** DEBUGMALLOC DUMP VEGE *******************************
diff --git a/Flow/reload.png b/Flow/reload.png
new file mode 100644
index 0000000..7babb73
Binary files /dev/null and b/Flow/reload.png differ
diff --git a/Flow/reload_clicked.png b/Flow/reload_clicked.png
new file mode 100644
index 0000000..b948b31
Binary files /dev/null and b/Flow/reload_clicked.png differ
diff --git a/Flow/scores.txt b/Flow/scores.txt
new file mode 100644
index 0000000..f24e83b
--- /dev/null
+++ b/Flow/scores.txt
@@ -0,0 +1 @@
+4,6,0
\ No newline at end of file
diff --git a/Flow/star.png b/Flow/star.png
new file mode 100644
index 0000000..feb8359
Binary files /dev/null and b/Flow/star.png differ
diff --git a/Flow/userLevels.txt b/Flow/userLevels.txt
new file mode 100644
index 0000000..ecafc7e
--- /dev/null
+++ b/Flow/userLevels.txt
@@ -0,0 +1,2 @@
+{{1,2,2,2},3,0,0}
+{{1,1,1,5},{2,1,2,4},{3,1,3,4},5,0,0}
\ No newline at end of file