Commits

lanael  committed 2a31b9b

* Nouveau format de maps et améliorations diverses :
* taille des maps minimum ( l'écran: 20x14 tiles )
* réorganisation des outils de l'éditeur + ajout option "save as" + ajout formulaire de saisie des paramêtres de la map ( nom, auteur, temps )
* mode Test depuis l'éditeur ( lance une nouvelle partie sur la map en cours d'édition )
* Menu Options, avec tentative de "Toggle Fullscreen" ( pb sous linux avec ma config )
* Le changement du volume affiche des "Messages"

  • Participants
  • Parent commits 462cc48

Comments (0)

Files changed (22)

File RWKC_linux.cbp

 		<Unit filename="src/gameobjects/updatedgameobject.d" />
 		<Unit filename="src/gameobjects/wall.d" />
 		<Unit filename="src/gamepart.d" />
+		<Unit filename="src/gamewon.d" />
 		<Unit filename="src/graphics.d" />
 		<Unit filename="src/gui.d" />
 		<Unit filename="src/inputactions.d" />

File maps/beurk.txt

-#####################
-#        u u        #
-#     ==     v      #
-#     ==            #
-#     12      12    #
-#   X 34j @   34 J  #
-# =#=#=#=#=#=#=#=#=##
-# #=#=#=#=#=#=#=#=#=#
-#                   #
-#                   #
-#                   #
-#                   #
-#                   #
-#                   #
-#                   #
-#####################

File maps/evil.map

+name="evil"
+author="mad"
+par_time = 0
+map = {
+"########################################"
+"#                                      #"
+"#  ##         ##                       #"
+"#  ##*       *##                       #"
+"#   #**     **#                        #"
+"#    #*#   #*#                         #"
+"#     ##   ##                          #"
+"#                                      #"
+"#  ##        ##                        #"
+"#  #=#      #=#                        #"
+"#   ########=#                         #"
+"#    #=#=#=##                          #"
+"#     ##=#=#                           #"
+"#      ####                            #"
+"#                                      #"
+"#                                      #"
+"#                                      #"
+"#                                      #"
+"#                                      #"
+"#                                      #"
+"########################################" }
+

File maps/map.map

+name = "Main test map"
+author = "Us"
+par_time = 600 ; un parametre
+map = {
+"########################################################"
+"#      c                                               #"
+"#    ###12######12### #       ******                   #"
+"#      #34####123412# #                                #"
+"#   #  #12##1234##34# #         m  c                   #"
+"#      #34##341212### #       ##3412      S &          #"
+"###    #1212##3434### #       ####34     ===121212     #"
+"#      #3434          #       121212     ###343434     #"
+"#   ######## ##########       343434     #########     #"
+"#      ##### #    u                            ###     #"
+"# ###  ##### #       c           =12====       ###     #"
+"#      ## X  #   #1212####       #343434               #"
+"#     ########  <#3434####       ###1212               #"
+"#     12#       ##12##12##       #123434####12         #"
+"#    #34#        #341234##       #341212####34         #"
+"#^   # ## @  V    ##34####       #123434#         12##1#"
+"#   ############   #1212####12   #34#####      s  34##3#"
+"#   12#  u          343412##34   #12##12#     ##121212##"
+"# v#34         <     ###341212   #34##34#     ##343434##"
+"# ###         ############3434   ###1212#              #"
+"#  #          #        ##~~~~#   ###3434#              #"
+"12>r        ###         #~~~~#   ########   ##         #"
+"34##  R                  #~~~#       g      ##         #"
+"#     B  H                #~~#       g          ####   #"
+"#     #######              ###    G  g       J         #"
+"#      b        >           rr   #121212#gg####,,,,,,,,#"
+"#,,,,,,#     ######        crr  ##343434#  ####~~~~~~~~#"
+"#~~~~~~#             #   ######            #####12####1#"
+"#~~~~~~#                12#~~###12##1212####12123412123#"
+"#~~~~~~#   j    <      #34#~~###34##3434####34341234341#"
+"########################################################"
+}
+

File maps/map2.map

+name = "map2"
+author = "neveu"
+par_time = 1200
+map = { 
+"########################################################"
+"#                                                      ="
+"# X  ===== >>>>>>>>>>>>>>>>>>>>>>>#                    ="
+"#============================================= ======= #"
+"#                                                      #"
+"#                                                      #"
+"#                                                      #"
+"#                                                      #"
+"#  121212121212121212121                               #"
+"#==3434343434343u343434u==========                     #"
+"#                                =#################### #"
+"#                                    12121212121212121 1"
+"#    @                               34343434343434343 3"
+"# ==========                                        =  #"
+"#                                                   =  #"
+"#                     S                            c=  #"
+"#              =============                ========== #"
+"#                  ==========               =        = #"
+"#       &          =       u                         = #"
+"#,,,,,,,,,,,,,=                             =        = #"
+"#~~~~~~~~~~~~~=                                   &  = #"
+"#==============                       ================ #"
+"#                              ========              = #"
+"#                              =      =              = #"
+"#                           J  =   &  =              = #"
+"#                        ========== ===              = #"
+"#                 m          u                       = #"
+"# ,,,,,,,,,,,,,,,,=               ==================== #"
+"# ~~~~~~~~~~~~~~~~=               =            =       #"
+"# ~~~~~~~~~~~~~~~~=               =            =       #"
+"# ~~~~~~~~~~~~~~~~=  &         c        &&&   V  &&&&  #"
+"########################################################"
+}

File maps/mini.txt

-#################
-#     #####    =#
-#      ###     =#
-#       #      =#
-#       u      =#
-# @          X =#
-#===  J     ###=#
-#    =#=       =#
-#,,,,,,,,,,,,,,=#
-#~~~~~~~~~~~~~~=#
-#~~~~~~~~~~~~~~=#
-#===============#
-#################

File maps/nouveau_test.map

+name = " petittest"
+author = " moi"
+par_time = 250
+map = { 
+"####################"
+"#,,,#          X#  #"
+"#~~~#        ####  #"
+"#~~~#           #  #"
+"#~~~#   &       #  #"
+"#~~~#  ======   #  #"
+"##### Jc        #  #"
+"#     12        #  #"
+"#     34******  #  #"
+"#  j          @ #  #"
+"#################  #"
+"#                  #"
+"#                  #"
+"#                  #"
+"####################"
+}

File maps/pouet.txt

-########################################
-#                                      #
-#                                      #
-#    #                                 #
-#   ##    ##                           #
-#   ## ##  ##                          #
-#   ## ##   ##                         #
-#   ##       ##                        #
-#   ##       ##                        #
-#   ##       ##                        #
-#   ## ##   ###                        #
-#   ## ##  ###                         #
-#   ##    ###                          #
-#    #    ##                           #
-#                                      #
-#                                      #
-#                                      #
-#                                      #
-#                                      #
-#                                      #
-########################################

File maps/redcross.txt

-########################################
-#        **                            #
-#        **                            #
-#        **                            #
-#        **                            #
-#        **                            #
-#   ************                       #
-#   ************                       #
-#        **                            #
-#        **                            #
-#        **                            #
-#        **                            #
-#        **                            #
-#                                      #
-#                                      #
-#                                      #
-#                                      #
-#                                      #
-#                                      #
-#                                      #
-########################################

File maps/test_a.txt

-####################
-#                  #
-#                  #
-#                  #
-#                  #
-#                  #
-                   
-#                  #
-#                  #
-#                  #
-#                  #
-#                  #
-#                  #
-#                  #
-####################

File src/editor.d

 	ActionType mAction;
 	string mPointerIcon;
 	char mTileCode;
-	bool mInGame;
+	bool mFromGame;
 	SDL_Rect mHighlightRect;
 	SDL_Surface* mHighlight;
 	MapNameDialog mMapNameDialog;
 	MapListDialog mMapListDialog;
+	ParamsDialog mParamsDialog;
+	string mPreviousPointer;
 	Alert mAlert;
 
 	void delegate() fMode;
 	void delegate() fPreviousMode;
 
-	this( bool inGame=false ) {
+	this( bool fromGame=false ) {
 		SDL_WM_GrabInput(SDL_GRAB_ON);
 
-		mInGame = inGame;
+		// if coming from game ( test mode ), reload the map
+		// else load an empty map.
+		mFromGame = fromGame;
+		string map_name;
+		if( gMap !is null ) {
+			 map_name = gMap.name;
+			delete gMap;
+		}
+		if( fromGame && map_name.length>0 ) new Map( map_name );
+		else new Map("");
 
 		gView.setTarget( null );
 
 		gView.update();
 		gView.draw();
 		fMode();
-		checkQuit();
+		//checkQuit();
 	}
 
 	void editMode() {
 		gInputManager.getMousePositionRelative( x,y );
 		GUI.instance.update( x,y, gInputManager.isMouseButtonUp(SDL_BUTTON_LEFT) );
 
+		checkEscapeToQuit();
+
 		if( gInputManager.isMouseButtonUp(SDL_BUTTON_MIDDLE) ) {
 			mTools = new EditorTools;
 			GUI.instance.changePointer( "pointer.png" );
 		}
 
 		// do action if click
-		if( gInputManager.isMouseButtonPressed(SDL_BUTTON_LEFT) ) {
+		bool action;
+		if( mAction == ActionType.ADD_TILE || mAction == ActionType.DELETE_TILE ) {
+			action=gInputManager.isMouseButtonPressed(SDL_BUTTON_LEFT);
+		}
+		else {
+			action=gInputManager.isMouseButtonUp(SDL_BUTTON_LEFT);
+		}
+		if( action ) {
 			switch( mAction ) {
 				case ActionType.INSERT_COLUMN:
 					gMap.insertColumn( xMap + xOffset );
 		if( gInputManager.isMouseButtonUp(SDL_BUTTON_MIDDLE) ) {
 			delete(mTools);
 			fMode = &editMode;
+			if( mPointerIcon.length > 0 ) GUI.instance.changePointer( mPointerIcon );
 			return;
 		}
 
 						saveMap();
 					}
 					else {
-						mMapNameDialog = new MapNameDialog();
+						mMapNameDialog = new MapNameDialog("");
 						fMode=&saveAsMode;
 					}
 				break;
 
 				case ActionType.TEST_MAP:
-					mAlert = new Alert("Not implemented yet !");
+					//mAlert = new Alert("Not implemented yet !");
 					//fMode=&mapTestMode;
-					fMode = &alertMode;
-					fPreviousMode = &editMode;
+					mState = GAMEPART_STATE.CONTINUE;
+					//fMode = &alertMode;
+					//fPreviousMode = &editMode;
 				break;
 
 				case ActionType.QUIT_EDITOR:
 					quit();
 				break;
 
-				default:
+				case ActionType.PARAMS:
+					mParamsDialog = new ParamsDialog(gMap.mName, gMap.mAuthor, gMap.mParTime );
+					fMode = &paramsMode;
+				break;
+
+				case ActionType.SAVE_AS:
+					mMapNameDialog = new MapNameDialog(gMap.name);
+					fMode=&saveAsMode;
+				break;
+
+				case ActionType.INSERT_COLUMN:
+				case ActionType.DELETE_COLUMN:
+				case ActionType.INSERT_LINE:
+				case ActionType.DELETE_LINE:
+				case ActionType.ADD_TILE:
+				case ActionType.DELETE_TILE:
 					mPointerIcon = mTools.getBitmap();
 					GUI.instance.changePointer( mPointerIcon );
 					mTileCode = mTools.getCode();
 					fMode = &editMode;
 				break;
+
+				default:
+				break;
 			}
 
 			delete( mTools );
 		}
 	}
 
+	/*
 	void checkQuit() {
-		if( mInGame && gInputManager.isReleased(IMA_TOGGLE_EDITOR) ) {
+		if( gInputManager.isReleased(IMA_TOGGLE_EDITOR) ) {
 			quit();
 		}
-		if( gInputManager.isReleased( IMA_BACK )) {
-			quit();
-		}
+		//if( gInputManager.isReleased( IMA_BACK )) {	quit();	}
+	}
+	*/
+
+	void checkEscapeToQuit() {
+		if( gInputManager.isReleased( IMA_BACK )) {	quit();	}
 	}
 
-
 	void quit() {
-		if( mInGame)
+		/*if( mFromGame)
 			mState = GAMEPART_STATE.CONTINUE;
-		else
+		else */
 			mState = GAMEPART_STATE.MAIN_MENU;
 	}
 
 
 		if( mMapNameDialog.done() ) {
 			if( mMapNameDialog.ok && mMapNameDialog.text != "" ) {
-				gMap.name= std.string.strip(mMapNameDialog.text)~".txt";
+				gMap.name= std.string.strip(mMapNameDialog.text);
 				saveMap();
 			}
 			else {
 
 	void saveMap() {
 		if( gMap.save() ) {
-			mAlert = new Alert( "map '"~mMapNameDialog.text~"' saved !" );
+			mAlert = new Alert( "map '"~gMap.name~"' saved !" );
 		}
 		else {
 			mAlert = new Alert( "Error while saving map !", "" );
 		}
 	}
 
+
+	void paramsMode() {
+		injectMouse();
+		if( mParamsDialog.done() ) {
+
+			if( mParamsDialog.ok ) {
+				gMap.mName = mParamsDialog.name;
+				gMap.mAuthor = mParamsDialog.author;
+				gMap.mParTime = mParamsDialog.par_time;
+			}
+
+			fMode = &editMode;
+			delete(mParamsDialog);
+		}
+	}
+
 	void injectMouse() {
 		int x,y;
 		gInputManager.getMousePositionRelative( x,y );
 		mHighlightRect.y = cast(short)y;
 		SDL_BlitSurface( mHighlight, null, gScreen.mSurface, &mHighlightRect );
 	}
+
 }

File src/editoractions.d

 	DELETE_TILE,
 	LOAD_MAP,
 	SAVE_MAP,
+	SAVE_AS,
 	TEST_MAP,
+	PARAMS,
 	QUIT_EDITOR
 };

File src/editordialogs.d

 
 import gui;
 import map;
+import inputmanager;
+import gameconstants;
 
 class Dialog {
 
 		mOkButton = mPanel.addTextButton( "OK", "", this );
 	}
 
+	bool done() {
+		if( gInputManager.isReleased(IMA_ACTION)) { mDone = true; }
+		if( gInputManager.isReleased(IMA_BACK)) { mDone = true; }
+
+		return( mDone );
+	}
+
 	void onButtonPressed( GUI.Button b ) {
 		mDone = true;
 	}
 }
 
+class ParamsDialog: public Dialog, GUI.ButtonListener {
+	GUI.TextInput mName;
+	GUI.TextInput mAuthor;
+	GUI.TextInput mTime;
+	GUI.Button mOkButton;
+
+	bool mOk;
+
+	this( string name, string author, uint par_time ) {
+		super();
+		mPanel.addLabel( "Map name :" );
+		mName = mPanel.addTextInput( 400, name );
+		mPanel.nl();
+		mPanel.addLabel( "Author :" );
+		mAuthor = mPanel.addTextInput( 400, author );
+		mPanel.nl();
+		mPanel.addLabel( "Par Time :" );
+		mTime = mPanel.addTextInput( 100, std.string.toString(par_time) );
+		mPanel.nl();
+		mPanel.nl();
+		mOkButton = mPanel.addTextButton( "OK", "", this );
+		mPanel.addTextButton( "Cancel", "", this );
+	}
+
+	bool ok() { return mOk; }
+
+	bool done() {
+		if( gInputManager.isReleased(IMA_ACTION)) { mOk=true; mDone = true; }
+		if( gInputManager.isReleased(IMA_BACK)) { mDone = true; }
+		return( mDone );
+	}
+
+	void onButtonPressed( GUI.Button b ) {
+		mDone = true;
+		if( b == mOkButton ) {
+			mOk = true;
+		}
+	}
+
+	string name() { return mName.getText(); }
+	string author() { return mAuthor.getText(); }
+	uint par_time() {
+		int t;
+		try {
+			t= std.conv.toInt(std.string.strip(mTime.getText()));
+		}
+		catch(Error e ) {
+			t=0;
+		}
+		return t;
+	}
+}
+
+
 class MapNameDialog: public Dialog, GUI.ButtonListener {
 
 	GUI.TextInput mTextInput;
 	GUI.Button mCancelButton;
 	bool mOk;
 
-	this() {
+	this( string name ) {
 		super();
-		mPanel.addLabel("Map name :" );
+		mPanel.addLabel("Map filename :" );
 		mPanel.nl();
-		mTextInput = mPanel.addTextInput( 200, "" );
+		mTextInput = mPanel.addTextInput( 200, name );
 		mPanel.nl();
 		mPanel.nl();
 		mOkButton = mPanel.addTextButton( "OK", "", this );
 
 	bool ok() { return mOk; }
 
+	bool done() {
+		if( gInputManager.isReleased(IMA_ACTION)) { mOk=true; mDone = true; }
+		if( gInputManager.isReleased(IMA_BACK)) { mDone = true; }
+		return( mDone );
+	}
+
 	void onButtonPressed( GUI.Button b ) {
 		if( b == mOkButton ) {
 			mOk = true;
 
 	bool ok() { return mOk; }
 
+	bool done() {
+		if( gInputManager.isReleased(IMA_ACTION)) { mOk=true; mDone = true; }
+		if( gInputManager.isReleased(IMA_BACK)) { mDone = true; }
+		return( mDone );
+	}
+
 	void onSelectedItem( int item ) {
 		mDone = true;
 		if( item >= 0 ) {

File src/editortools.d

 	bool mDone = false;
 
 	this() {
-
 		initActions();
-
-		mActionsPanel = GUI.instance.addPanel( 0,0 );
-		foreach( inout action; mActions ) {
-			if( action.type == ActionType.NOTHING )
-				mActionsPanel.nl();
-			else
-				action.button = mActionsPanel.addTextButton( action.caption, "", this );
-		}
-
 		initTiles();
-
-		mTilesPanel = GUI.instance.addPanel( 0, 100 );
-		foreach( int i, inout tile; mTiles ) {
-			tile.button = mTilesPanel.addIconButton( tile.bitmap, this );
-			if( (i+1)%17 == 0) mTilesPanel.nl();
-		}
-
-		mSelectedAction = ActionType.NOTHING;
 	}
 
 	~this() {
 	}
 
 	void initActions() {
-		mActions ~= Action( "Load map", "", ActionType.LOAD_MAP );
-		mActions ~= Action( "Save map", "", ActionType.SAVE_MAP );
-		mActions ~= Action( "Test map", "", ActionType.TEST_MAP );
-		mActions ~= Action( "Quit editor", "", ActionType.QUIT_EDITOR );
+		mActions ~= Action( "Load", "", ActionType.LOAD_MAP );
+		mActions ~= Action( "Save", "", ActionType.SAVE_MAP );
+		mActions ~= Action( "Save As", "", ActionType.SAVE_AS );
+		mActions ~= Action( "Test", "", ActionType.TEST_MAP );
+		mActions ~= Action( "Params", "", ActionType.PARAMS );
+		mActions ~= Action( "Quit ", "", ActionType.QUIT_EDITOR );
 		mActions ~= Action( "", "", ActionType.NOTHING );
 		mActions ~= Action( "Insert Column", "column_add.png", ActionType.INSERT_COLUMN );
 		mActions ~= Action( "Insert Line", "row_add.png", ActionType.INSERT_LINE );
 		mActions ~= Action( "Delete Column", "column_delete.png", ActionType.DELETE_COLUMN );
 		mActions ~= Action( "Delete Line", "row_delete.png", ActionType.DELETE_LINE );
 
+		mActionsPanel = GUI.instance.addPanel( 0,0 );
+		mActionsPanel.setAlign( GUI.Panel.ALIGN.MIN, GUI.Panel.ALIGN.CENTERED );
+		foreach( inout action; mActions ) {
+			if( action.type == ActionType.NOTHING ) {
+				mActionsPanel.addLabel( "--------" );
+				mActionsPanel.nl();
+			}
+			else {
+				action.button = mActionsPanel.addTextButton( action.caption, "", this );
+				mActionsPanel.nl();
+			}
+		}
+
 	}
 
 	void initTiles() {
-
 		mTiles ~= Tile( "wall1.png", '#' );
 		mTiles ~= Tile( "wall2.png", '=' );
 		mTiles ~= Tile( "bigwall1_1.png", '1' );
 		mTiles ~= Tile( "bigwall1_3.png", '3');
 		mTiles ~= Tile( "bigwall1_4.png", '4' );
 		mTiles ~= Tile( "brick.png", '*' );
+		mTiles ~= Tile( "fluid_top_full_000001.png", ',' );
+		mTiles ~= Tile( "fluid_000001.png", '~' );
+		mTiles ~= Tile( "", '\0');
 		mTiles ~= Tile( "bluedoor.png", 'b' );
 		mTiles ~= Tile( "greendoor.png", 'g' );
 		mTiles ~= Tile( "reddoor.png", 'r' );
-		mTiles ~= Tile( "boss_icon.png", '&' );
+		mTiles ~= Tile( "bluekey.png", 'B' );
+		mTiles ~= Tile( "greenkey.png", 'G' );
+		mTiles ~= Tile( "redkey.png", 'R' );
+		mTiles ~= Tile( "", '\0');
+		mTiles ~= Tile( "hero_right_000002.png", '@' );
+		mTiles ~= Tile( "princess_000001.png", 'X' );
 		mTiles ~= Tile( "editor_ennemi_h_left.png", '<' );
 		mTiles ~= Tile( "editor_ennemi_h_right.png", '>' );
-		mTiles ~= Tile( "hero_right_000002.png", '@' );
-		mTiles ~= Tile( "fluid_top_full_000001.png", ',' );
-		mTiles ~= Tile( "fluid_000001.png", '~' );
 		mTiles ~= Tile( "editor_ennemi_v_up.png", '^' );
 		mTiles ~= Tile( "editor_ennemi_v_down.png", 'v' );
 		mTiles ~= Tile( "ennemy_c_000001.png", 'u' );
-		mTiles ~= Tile( "princess_000001.png", 'X' );
+		mTiles ~= Tile( "boss_icon.png", '&' );
+		mTiles ~= Tile( "save_0.png", 'c' );
+		mTiles ~= Tile( "", '\0');
 		mTiles ~= Tile( "jump.png", 'j' );
 		mTiles ~= Tile( "doublejump.png", 'J' );
 		mTiles ~= Tile( "shot.png", 's' );
 		mTiles ~= Tile( "powerup_missile.png", 'm' );
 		mTiles ~= Tile( "horizontal_speed.png", 'H' );
 		mTiles ~= Tile( "vertical_speed.png", 'V' );
-		mTiles ~= Tile( "bluekey.png", 'B' );
-		mTiles ~= Tile( "greenkey.png", 'G' );
-		mTiles ~= Tile( "redkey.png", 'R' );
-		mTiles ~= Tile( "save_0.png", 'c' );
+		mTiles ~= Tile( "", '\0');
 		mTiles ~= Tile( "delete.png", '\0' );
 
+		mTilesPanel = GUI.instance.addPanel( 300, 0 );
+		mTilesPanel.setAlign( GUI.Panel.ALIGN.MAX, GUI.Panel.ALIGN.CENTERED );
+		foreach( inout tile; mTiles ) {
+			if( tile.bitmap == "" ) {
+				mTilesPanel.nl();
+			}
+			else {
+				tile.button = mTilesPanel.addIconButton( tile.bitmap, this );
+				//if( (i+1)%10 == 0) mTilesPanel.nl();
+			}
+		}
+
+		mSelectedAction = ActionType.NOTHING;
+
 	}
 
 	void onButtonPressed( GUI.Button b ) {
 		mDone = true;
 
 		foreach( tile; mTiles ) {
-			if( tile.button == b ) {
+			if( tile.button !is null && tile.button == b ) {
 				mSelectedBitmap = tile.bitmap;
 				mSelectedCode = tile.mapCode;
 
 					mSelectedAction = ActionType.ADD_TILE;
 				else
 					mSelectedAction = ActionType.DELETE_TILE;
-
 				return;
 			}
 		}
 
 		foreach( action; mActions ) {
-			if( action.type != ActionType.NOTHING && action.button == b ) {
+			if( action.button !is null && action.type != ActionType.NOTHING && action.button == b ) {
 				mSelectedAction = action.type;
 				mSelectedBitmap = action.bitmap;
 				return;
     GAMEPART_STATE mState = GAMEPART_STATE.GAME;
     SDL_Surface* mBg;
     GamePart mSubPart;
+	bool mTestMode;
 
-    this() {
+    this( bool testMode ) {
         gGame = this;
         mBg = gTextureManager.get( "background.png" );
         new Chrono;
 		gSoundManager.play( gSoundManager.get("start.ogg"), MIX_MAX_VOLUME/2 );
+		mTestMode = testMode;
     }
 
     ~this() {
 
     void processInput() {
 
-        if( gInputManager.isReleased(IMA_BACK) ||
-            gInputManager.isReleased(IMA_PAUSE) ) {
-                mSubPart = new PauseMenu;
+		if( mTestMode ) {
+			if( gInputManager.isReleased(IMA_BACK) )
+				mState = GAMEPART_STATE.EDITOR_INGAME;
+		}
+		else {
+
+			if( gInputManager.isReleased(IMA_BACK) ||
+				gInputManager.isReleased(IMA_PAUSE) ) {
+					mSubPart = new PauseMenu;
+			}
+		}
+
+        if( gInputManager.isReleased(IMA_VOLUME_UP)) {
+        	gSoundManager.volumeUp();
+        	Messages.add("Sound volume : " ~ std.string.toString(gSoundManager.getVolume()) );
         }
-
-        if( gInputManager.isReleased(IMA_VOLUME_UP)) gSoundManager.volumeUp();
-        if( gInputManager.isReleased(IMA_VOLUME_DOWN)) gSoundManager.volumeDown();
+        if( gInputManager.isReleased(IMA_VOLUME_DOWN)) {
+        	gSoundManager.volumeDown();
+        	Messages.add( "Sound Volume : " ~ std.string.toString( gSoundManager.getVolume()) );
+        }
 
         if( gInputManager.isReleased(IMA_TOGGLE_EDITOR)) mState = GAMEPART_STATE.EDITOR_INGAME;
 

File src/gamemanager.d

 
 			case GAMEPART_STATE.NEW_GAME:
 				delete( mGamePart );
-				mGamePart = new Game();
+				mGamePart = new Game(false);
 			break;
 
 			case GAMEPART_STATE.GAME_WON:
 
 			case GAMEPART_STATE.CONTINUE:
 				delete( mGamePart );
-				mGamePart = new Game();
+				mGamePart = new Game(true);
+			break;
+
+			case GAMEPART_STATE.MENU_OPTIONS:
+				delete( mGamePart );
+				mGamePart = new MenuOptions();
 			break;
 
 			case GAMEPART_STATE.EXIT:

File src/gamewon.d

+
+module gamewon;
+
+import gamepart;
+
+class GameWon : GamePart {
+
+	GAMEPART_STATE mState;
+
+	void update() {
+
+	}
+
+	GAMEPART_STATE getState() {
+		return( mState );
+	}
+
+}
 		void render();
 	}
 
+	interface Clickable {
+		bool isIn(int x, int y);
+	}
+
+	interface Typeable {
+		void injectKey( ubyte key );
+		string getText();
+	}
+
+	interface ButtonListener {
+		void onButtonPressed( Button b);
+	}
+
+
 	class Label: public Widget, Renderable {
 		TextSurface mText;
 		this( Widget parent, int x, int y, string label, string fontid ) {
 		}
 	}
 
-	interface Typeable {
-		void injectKey( ubyte key );
-		string getText();
-	}
-
-	class TextInput: public Label, Typeable {
+	class TextInput: public Label, Typeable, Clickable {
 		uint mWidth;
 		TextSurface mCaret;
+		SDL_Surface* mHl;
+		bool mSelected;
 
-		this( Widget parent, int x, int y, uint width, string text="", string fontid="" ) {
+		this( Widget parent, int x, int y, uint maxWidth, string text="", string fontid="" ) {
 			super( parent, x, y, text, fontid );
-			mWidth = width;
+			mWidth = maxWidth;
 			mCaret = new TextSurface("|");
+			mHl = mGraphics.createTransparentSurface( mWidth+2, mH+4, 64 );
 		}
 
 		~this() {
 			delete( mCaret );
+			SDL_FreeSurface( mHl );
 		}
 
 		void injectKey( ubyte key ) {
 				mText.setText( mText.getText()[0 .. $-1] );
 				mW = mText.w;
 			}
-			if( key > 32 && key<127 && (mText.w < mWidth) ) {
+			if( key > 32 && key<127 && (mText.w < mWidth-10 ) ) {
 				mText.setText( mText.getText() ~ cast(char)key );
 				mW = mText.w;
 			}
 
 		string getText() { return( mText.getText() ); }
 
+		bool isIn( int x, int y ) {
+			return( x>mX && x<mX+mWidth
+				&& y>mY && y<mY+mCaret.h );
+		}
+
+		void selected( bool select ) { mSelected = select; }
+
 		void render() {
+			if( mSelected ) mGraphics.drawImage( mHl, mX-2, mY-2 );
 			super.render();
-			mCaret.draw( mX+mW, mY );
+			if( mSelected ) mCaret.draw( mX+mW, mY );
 			mGraphics.drawBox( cast(short)mX-2, cast(short)mY-2, cast(ushort)mWidth+4, cast(ushort)mH+4 );
 		}
 	}
 
-	interface Clickable {
-		bool isIn(int x, int y);
-	}
 
 	class Button: public Widget, Renderable, Clickable {
 
 		}
 
 		void buildHighlight( ubyte power=64 ) {
-
 			mHl = mGraphics.createTransparentSurface( mW, mH, power );
-			/*
-			mHl = SDL_CreateRGBSurface(
-				SDL_HWSURFACE,
-				mW, mH, 32,
-				0x00, 0x00, 0x00, 0x00 );
-			SDL_FillRect( mHl, null, 0xFFFFFFFF );
-			SDL_SetAlpha( mHl, SDL_SRCALPHA, power ); */
 		}
 
 		bool isIn( int x, int y ) {
 
 	}
 
-	interface ButtonListener {
-		void onButtonPressed( Button b);
-	}
+
 
 	class IconButton: public Button {
 
 		SDL_Surface* mBg;
 		SDL_Rect mRect;
 		Button mHighlighted;
+		uint mSelectedTextInput;
 
 		enum ALIGN { FREE, MIN, MAX, CENTERED };
 		ALIGN mHAlign = ALIGN.FREE;
 			TextInput ti = new TextInput( this, mXInsert, mYInsert, width, text, fontid );
 			mTextInputs ~= ti;
 			resize( ti.mWidth, ti.height );
+			if( mTextInputs.length == 1 ) { mSelectedTextInput = 0; ti.selected = true; }
 			return(ti);
 		}
 
 		void nl() {
 			mXInsert = mMargin;
 			mYInsert += mLineHeight + mLineGap;
-			mLineHeight = 0;
+			mLineHeight = 10;
 		}
 
 		private void adjustLineHeight( int h ) {
-			if( h > mLineHeight ) mLineHeight = h;
+			if( h+mLineGap > mLineHeight ) mLineHeight = h+mLineGap;
 		}
 
 		private void resize( int w, int h ) {
 			int prevX = mX;
 			int prevY = mY;
 
-			if( mHAlign == ALIGN.FREE ) mX = x; { moved = true; }
-			if( mVAlign == ALIGN.FREE ) mY = y; { moved = true; }
+			if( mHAlign == ALIGN.FREE ) { mX = x; if(prevX != mX) moved = true; }
+			if( mVAlign == ALIGN.FREE ) { mY = y; if(prevY != mY) moved = true; }
 
 			moved = doAlign( mX, mHAlign, mW, gScreen.WIDTH ) || moved;
 			moved = doAlign( mY, mVAlign, mH, gScreen.HEIGHT ) || moved;
 					ti.move( offX, offY );
 				}
 				foreach( inout p; mPanels ) {
-					p.move( mX + offX, mY + offY );
+					p.move( p.mX + offX, p.mY + offY );
 				}
 			}
 		}
 				break;
 
 				case ALIGN.MIN:
+					if( coord != 0) moved = true;
 					coord = 0;
-					moved = true;
 				break;
 
 				case ALIGN.MAX:
+					if( coord != (max - size) ) moved = true;
 					coord = max - size;
-					moved = true;
 				break;
 
 				case ALIGN.CENTERED:
+					if( coord != (max-size)/2 ) moved = true;
 					coord = (max - size )/2;
-					moved = true;
 				break;
 			}
 			return( moved );
 					if( clicked ) b.notifyListener();
 				}
 			}
+
+			if( clicked ) {
+				foreach( i, ti; mTextInputs ) {
+					if( ti.isIn(x,y) ) {
+						mTextInputs[mSelectedTextInput].selected = false;
+						mSelectedTextInput = i;
+						ti.selected = true;
+					}
+				}
+			}
 		}
 
 		void injectKey( ubyte key ) {
-			foreach( ti; mTextInputs ) {
-				ti.injectKey( key );
+
+			if( key == 9 ) {
+				mTextInputs[mSelectedTextInput].selected = false;
+				mSelectedTextInput = (mSelectedTextInput+1) % mTextInputs.length;
+				mTextInputs[mSelectedTextInput].selected = true;
 			}
+
+			if( mTextInputs.length > 0 )
+				mTextInputs[mSelectedTextInput].injectKey( key );
 		}
 
 	}

File src/inputmanager.d

-
 module inputmanager;
 
 import derelict.sdl.sdl;
 
 class InputManager {
 
-	InputAction[] mInputActions;
-	uint[char[]] mBindings;
-	ubyte* mSDLKeys;
-	// char[][] mPossibleActions;
-	char[] mConfigFilename;
-	ubyte mLastPressedKey;
+    InputAction[] mInputActions;
+    uint[char[]] mBindings;
+    ubyte* mSDLKeys;
+    // char[][] mPossibleActions;
+    char[] mConfigFilename;
+    ubyte mLastPressedKey;
 
-	bool[8] mMouseButtonsUp;
-	bool[8] mMouseButtonsDown;
+    bool[8] mMouseButtonsUp;
+    bool[8] mMouseButtonsDown;
 
-	this( char[] configFilename ) {
-		gInputManager = this;
+    this( char[] configFilename ) {
+        gInputManager = this;
 
-		mConfigFilename = configFilename;
+        mConfigFilename = configFilename;
 
-		SDL_EnableKeyRepeat(0, 0 );
+        SDL_EnableKeyRepeat(0, 0 );
 
-		if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0 ) {
-	     	 // throw new SDLInitFailedException(
-			writefln("Unable to init SDL joystick: "); //  ~ toString(SDL_GetError()))  ;
+        if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) < 0 ) {
+            // throw new SDLInitFailedException(
+            writefln("Unable to init SDL joystick: "); //  ~ toString(SDL_GetError()))  ;
+        }
+        SDL_EnableUNICODE( SDL_ENABLE );
+    }
+
+    char[][] getActions() {
+        return( mBindings.keys );
+    }
+
+    void bind( uint actionId, char[] actionName ) {
+
+        mBindings[actionName] = actionId;
+
+        if ( mBindings.length > mInputActions.length ) mInputActions.length = mBindings.length;
+
+        debug(inputmanager) writefln( __FILE__, " : binding action \"", actionName,"\" (", actionId, ")" );
+        debug(inputmanager) writefln( __FILE__, " : ", mBindings.length, " bindings / ", mInputActions.length, " actions ");
+
+    }
+
+    void loadConfig() {
+        auto ic = new InputConfiguration();
+        if ( ! ic.load( mConfigFilename ))
+            throw new Error("Error while loading \"" ~ mConfigFilename ~ "\"");
+    }
+
+    void update() {
+        SDL_Event event;
+        mSDLKeys = SDL_GetKeyState(null);
+
+        foreach( action; mInputActions ) {
+            action.update();
+        }
+
+        mMouseButtonsUp[] = false;
+        mMouseButtonsDown[] = false;
+
+        mLastPressedKey = 0;
+
+        while ( SDL_PollEvent(&event) ) {
+
+            switch ( event.type ) {
+            case SDL_QUIT:
+                throw new Error( "quitting" );  // *tousse* *tousse*
+
+            case SDL_MOUSEBUTTONUP:
+                mMouseButtonsUp[event.button.button] = true;
+                break;
+
+            case SDL_MOUSEBUTTONDOWN:
+                mMouseButtonsDown[event.button.button] = true;
+                break;
+
+            case SDL_KEYDOWN:
+                mLastPressedKey = getKeyValue( event.key.keysym.unicode);
+                break;
+
+            default:
+                break;
+            }
+        }
+    }
+
+	ubyte getKeyValue( ushort unicode ) {
+		char ch;
+		if ( (unicode & 0xFF80) == 0 ) {
+		  return(cast(ubyte)(unicode & 0x7F));
 		}
-
-	}
-
-	char[][] getActions() {
-		return( mBindings.keys );
-	}
-
-	void bind( uint actionId, char[] actionName ) {
-
-		mBindings[actionName] = actionId;
-
-		if( mBindings.length > mInputActions.length ) mInputActions.length = mBindings.length;
-
-		debug(inputmanager) writefln( __FILE__, " : binding action \"", actionName,"\" (", actionId, ")" );
-		debug(inputmanager) writefln( __FILE__, " : ", mBindings.length, " bindings / ", mInputActions.length, " actions ");
-
-	}
-
-	void loadConfig() {
-		auto ic = new InputConfiguration();
-		if( ! ic.load( mConfigFilename ))
-			throw new Error("Error while loading \"" ~ mConfigFilename ~ "\"");
-	}
-
-	void update() {
-		SDL_Event event;
-		mSDLKeys = SDL_GetKeyState(null);
-
-		foreach( action; mInputActions ) {
-			action.update();
-		}
-
-		mMouseButtonsUp[] = false;
-		mMouseButtonsDown[] = false;
-
-		mLastPressedKey = 0;
-
-        while( SDL_PollEvent(&event) ) {
-
-        	switch( event.type ) {
-				case SDL_QUIT:
-					throw new Error( "quitting" );  // *tousse* *tousse*
-
-				case SDL_MOUSEBUTTONUP:
-					mMouseButtonsUp[event.button.button] = true;
-				break;
-
-				case SDL_MOUSEBUTTONDOWN:
-					mMouseButtonsDown[event.button.button] = true;
-				break;
-
-				case SDL_KEYUP:
-					mLastPressedKey = cast(ubyte)event.key.keysym.sym;
-				break;
-
-				default:
-				break;
-        	}
-        }
-	}
-
-	void getMousePositionRelative( out int x, out int y ) {
-		SDL_GetRelativeMouseState( &x, &y );
-	}
-
-	void getMousePosition( out int x, out int y ) {
-		SDL_GetMouseState( &x, & y);
-	}
-
-	bool isMouseButtonUp( ubyte button ) {
-	// button is one of SDL_BUTTON_LEFT, _RIGHT, _MIDDLE, _WHEELUP, etc..
-		return( mMouseButtonsUp[button] );
-	}
-
-	bool isMouseButtonDown( ubyte button ) {
-	// button is one of SDL_BUTTON_LEFT, _RIGHT, _MIDDLE, _WHEELUP, etc..
-		return( mMouseButtonsDown[button] );
-	}
-
-	bool isMouseButtonPressed( ubyte button ) {
-		ubyte state = SDL_GetMouseState( null, null );
-		switch( button ) {
-			case SDL_BUTTON_LEFT:
-				return( (state & SDL_BUTTON_LMASK)!=0 );
-
-			case SDL_BUTTON_RIGHT:
-				return( (state & SDL_BUTTON_RMASK)!=0 );
-
-			case SDL_BUTTON_MIDDLE:
-				return( (state & SDL_BUTTON_MMASK)!=0 );
-
-			default:
-				return( false );
+		else {
+		  return(0);
 		}
 	}
 
-	bool isPressed( uint action ) {
-		return( mInputActions[action].isPressed());
-	}
+    char getUnicodeValue( inout SDL_KeyboardEvent key ) {
+        // magic numbers courtesy of SDL docs :)
+        const int INTERNATIONAL_MASK = 0xFF80, UNICODE_MASK = 0x7F;
+        int uni = key.keysym.unicode;
+        if ( uni == 0 ) {
+        	// not translatable key (like up or down arrows)
+            // probably not useful as string input
+            // we could optionally use this to get some value
+            // for it: SDL_GetKeyName( key );
+            return 0;
+        }
+        if ( ( uni & INTERNATIONAL_MASK ) == 0 ) {
+            if ( SDL_GetModState() & KMOD_SHIFT ) {
+                return cast(char)(/*toupper(*/ uni & UNICODE_MASK /*)*/ );
+            } else {
+                return cast(char)(uni & UNICODE_MASK);
+            }
+        }
+        else {  // we have a funky international character. one we can't read :(
+                // we could do nothing, or we can just show some sign of input, like so:
+            return '?';
+        }
+    }
 
-	bool isReleased( uint action ) {
-		return( mInputActions[action].isUp());
-	}
 
-	bool isKeyPressed( uint keycode ) {
-		return( mSDLKeys[keycode] == SDL_PRESSED );
-	}
+    void getMousePositionRelative( out int x, out int y ) {
+        SDL_GetRelativeMouseState( &x, &y );
+    }
 
-	void setAction( char[] name, uint method, uint keycode,
-		uint joynum, uint button, uint axis, int dir, uint threshold ) {
+    void getMousePosition( out int x, out int y ) {
+        SDL_GetMouseState( &x, & y);
+    }
 
-		uint id = mBindings[ name ];
+    bool isMouseButtonUp( ubyte button ) {
+        // button is one of SDL_BUTTON_LEFT, _RIGHT, _MIDDLE, _WHEELUP, etc..
+        return( mMouseButtonsUp[button] );
+    }
 
-		debug(inputmanager) writefln( __FILE__, " : adding action \"", name,"\" (", id, ")" );
+    bool isMouseButtonDown( ubyte button ) {
+        // button is one of SDL_BUTTON_LEFT, _RIGHT, _MIDDLE, _WHEELUP, etc..
+        return( mMouseButtonsDown[button] );
+    }
 
-		switch( method ) {
-			case IAM_KEY:
-				mInputActions[ id ] = new KeyInputAction(keycode);
-			break;
+    bool isMouseButtonPressed( ubyte button ) {
+        ubyte state = SDL_GetMouseState( null, null );
+        switch ( button ) {
+        case SDL_BUTTON_LEFT:
+            return( (state & SDL_BUTTON_LMASK)!=0 );
 
-			case IAM_BUTTON:
-				mInputActions[ id ] = new ButtonInputAction( joynum, button );
-			break;
+        case SDL_BUTTON_RIGHT:
+            return( (state & SDL_BUTTON_RMASK)!=0 );
 
-			case IAM_AXIS:
-				mInputActions[ id ] = new AxisInputAction( joynum, axis, dir, threshold );
-			break;
-			default:
-			break;
-		}
-	}
+        case SDL_BUTTON_MIDDLE:
+            return( (state & SDL_BUTTON_MMASK)!=0 );
 
-	ubyte* getKeys() {
-		return( mSDLKeys );
-	}
+        default:
+            return( false );
+        }
+    }
 
-	ubyte getKeyUp() {
-		return( mLastPressedKey );
-	}
+    bool isPressed( uint action ) {
+        return( mInputActions[action].isPressed());
+    }
+
+    bool isReleased( uint action ) {
+        return( mInputActions[action].isUp());
+    }
+
+    bool isKeyPressed( uint keycode ) {
+        return( mSDLKeys[keycode] == SDL_PRESSED );
+    }
+
+    void setAction( char[] name, uint method, uint keycode,
+                    uint joynum, uint button, uint axis, int dir, uint threshold ) {
+
+        uint id = mBindings[ name ];
+
+        debug(inputmanager) writefln( __FILE__, " : adding action \"", name,"\" (", id, ")" );
+
+        switch ( method ) {
+        case IAM_KEY:
+            mInputActions[ id ] = new KeyInputAction(keycode);
+            break;
+
+        case IAM_BUTTON:
+            mInputActions[ id ] = new ButtonInputAction( joynum, button );
+            break;
+
+        case IAM_AXIS:
+            mInputActions[ id ] = new AxisInputAction( joynum, axis, dir, threshold );
+            break;
+        default:
+            break;
+        }
+    }
+
+    ubyte* getKeys() {
+        return( mSDLKeys );
+    }
+
+    ubyte getKeyUp() {
+        return( mLastPressedKey );
+    }
 }
 
 import gameobjectfactory;
 import gameobject;
 import objectlist;
+import lexer;
 
 debug import std.stdio;
 
 public static Map gMap;
-//public static MapList gMapList;
 
 class MapList {
 
 	string[] mFileNames;
 
 	this() {
-		mFileNames = std.file.listdir( "maps", RegExp(r".*.\.txt$") );
+		mFileNames = std.file.listdir( "maps", RegExp(r".*.\.map$") );
 		foreach( i, f; mFileNames ) {
 			// Remove "maps/"
 			mFileNames[i] = f[5..$];
 	int mWidth;				/** Width of the map in tiles */
 	int mHeight;			/** Height of the map in tiles */
 	string mMapFileName;
-	string[] mMapFile;
+	string[] mCharMap;
+
+	string mName;
+	string mAuthor;
+	uint mParTime;
 
 	const int TILE_WIDTH = 32;
 	const int TILE_HEIGHT = 32;
+
+	const int MIN_WIDTH = 20;
+	const int MIN_HEIGHT = 14;
 
     this(string mapFileName) {
         gMap = this;
 
-		if( mapFileName != "_new_" )
-			load( mapFileName );
-		else
+		if( mapFileName == "" || !load( mapFileName ) )
 			genEmptyMap();
 
 		debug print();
 
 	debug {
 		void print() {
-			foreach( row; mMapFile ) {
+			foreach( row; mCharMap ) {
 				writefln( row );
 			}
 		}
 
 	bool load( string mapFileName ) {
 
-		char[] file = cast(char[]) read("maps/" ~ mapFileName);
+		//char[] file = cast(char[]) read("maps/" ~ mapFileName);
 		mMapFileName = mapFileName;
 
-        mMapFile = file.splitlines();
+		writefln( "parsing ", mapFileName );
 
-        mHeight = mMapFile.length;
-        mWidth = mMapFile[0].length;
+		if( parse( "maps/" ~ mapFileName ) ) {
+			//mCharMap = file.splitlines();
+			mHeight = mCharMap.length;
+			mWidth = mCharMap[0].length;
+			return( true );
+		}
 
+		return( false );
+	}
 
-		return( true );
-	}
+
+	bool parse( string mapFileName ) {
+
+		bool ok=true;
+
+		void myerror( string error, int line ) {
+			writefln( "Parse error :", error, ", line:", line );
+			ok = false;
+		}
+
+		char[] var;
+		char[] file;
+		try {
+			file = cast(char[])read( mapFileName );
+		}
+		catch( FileException fe ) {
+			debug writefln( fe.msg );
+			return(false);
+		}
+
+		file ~= "\0\0";
+		auto lexer = new Lexer( file );
+		token tok;
+
+		lexer.nextToken(tok);
+
+		while( tok.type != TOK.EOF ) {
+
+			if( tok.type == TOK.COMMENT ) {
+				lexer.nextToken( tok );
+				continue;
+			}
+
+			if(tok.type != TOK.KEYWORD ) { myerror("expecting a keyword", tok.line);  break;}
+			var=tok.string;
+			lexer.nextToken(tok);
+			if(tok.type != TOK.ASSIGN ) { myerror("expecting '='", tok.line); break; }
+			lexer.nextToken(tok);
+
+			switch(var) {
+				case "name" :
+					if(tok.type != TOK.STRING ) myerror("expecting a string", tok.line);
+					else mName=tok.string;
+				break;
+
+				case "author" :
+					if(tok.type != TOK.STRING ) myerror("expecting a string", tok.line);
+					else mAuthor=tok.string;
+				break;
+				case "par_time" :
+					if(tok.type != TOK.NUMBER ) myerror("expecting an integer", tok.line);
+					else mParTime=std.conv.toInt(tok.string);
+				break;
+				case "map" :
+					if(tok.type != TOK.OPENBRACE ) myerror("expecting '{'", tok.line );
+					else {
+						lexer.nextToken(tok);
+						while(tok.type == TOK.STRING) {
+							mCharMap ~= tok.string;
+							lexer.nextToken(tok);
+						}
+						if(tok.type != TOK.CLOSEDBRACE ) myerror("expecting '}'", tok.line);
+					}
+				break;
+
+				default:
+					//writefln("Warning : Unknown keyword \"%s\" line %d in %s", var, tok.line, mapFileName );
+					myerror("unknown variable '"~ var ~"'", tok.line );
+				break;
+
+			}
+
+			if(!ok) break;
+
+			lexer.nextToken(tok);
+
+		} // while
+
+		return(ok);
+
+	}
+
 
 	bool save() {
 
 		char[] file;
-		foreach( line; mMapFile ) {
-			file ~= line ~ "\n";
+		file ~= "name = \"" ~ mName ~ "\"\n";
+		file ~= "author = \"" ~ mAuthor ~ "\"\n";
+		file ~= "par_time = " ~ std.string.toString(mParTime) ~ "\n";
+		file ~= "map = { \n";
+		foreach( line; mCharMap ) {
+			file ~= "\"" ~ line ~ "\"\n";
 		}
+		file ~= "}\n";
+
 		try {
 			/*
 			if( std.file.exists( "maps" )==0 && std.file.isdir("maps") )
 	}
 
 	void name( string filename ) {
+		if( std.regexp.rfind(filename, ".map$" ) == -1 ) {
+			filename ~= ".map";
+		}
 		mMapFileName = filename;
 	}
 
 	}
 
 	void genEmptyMap() {
-		uint h = 20;
-		uint w = 40;
+		uint h = MIN_HEIGHT;
+		uint w = MIN_WIDTH;
 
-		mMapFile ~= std.string.repeat( "#", w );
+		mCharMap ~= std.string.repeat( "#", w );
 		for( int i=0; i<=(h-2); i++ ) {
-			mMapFile ~= "#" ~ std.string.repeat( " ", w-2 ) ~ "#";
+			mCharMap ~= "#" ~ std.string.repeat( " ", w-2 ) ~ "#";
 		}
-		mMapFile ~= std.string.repeat( "#", w );
+		mCharMap ~= std.string.repeat( "#", w );
 
-		mHeight = mMapFile.length;
-		mWidth = mMapFile[0].length;
+		mHeight = mCharMap.length;
+		mWidth = mCharMap[0].length;
 
-		//mMapFile[std.random.rand()%(mWidth-3)+1][std.random.rand()%(mHeight-3)+1] = '@';
-		//mMapFile[std.random.rand()%(mWidth-3)+1][std.random.rand()%(mHeight-3)+1] = 'X';
+		//mCharMap[std.random.rand()%(mWidth-3)+1][std.random.rand()%(mHeight-3)+1] = '@';
+		//mCharMap[std.random.rand()%(mWidth-3)+1][std.random.rand()%(mHeight-3)+1] = 'X';
 
 		mMapFileName = "";
 	}
 			mMap[i].length = mWidth;
 
 
-		foreach( uint y, char[] line; mMapFile ) {
+		foreach( uint y, char[] line; mCharMap ) {
 
 			foreach( uint x, char c; line ) {
 
 		// update the mapfile
 		char[] emptyRow;
 		emptyRow = std.string.repeat(" ", mWidth-1 );
-		mMapFile = mMapFile[0 .. rownum] ~ emptyRow ~ mMapFile[rownum .. $];
+		mCharMap = mCharMap[0 .. rownum] ~ emptyRow ~ mCharMap[rownum .. $];
 
 		// update the map of gameobjects
 		ObjectList[] emptyRow2;
 
 
 	void deleteRow( uint rownum ) {
-		mMapFile = mMapFile[0 .. rownum] ~ mMapFile[rownum+1 .. $];
+
+		if( mHeight <= MIN_HEIGHT ) return;
+
+		mCharMap = mCharMap[0 .. rownum] ~ mCharMap[rownum+1 .. $];
 		// remove objects on this row
 		//foreach( cell; mMap[row] )
 			//foreach( o; cell ) removeObject(o);
 
 
 	void insertColumn( uint colnum ) {
-		foreach( inout row; mMapFile ) {
+		foreach( inout row; mCharMap ) {
 			row = row[0 .. colnum] ~ ' ' ~ row[colnum .. $];
 		}
 
 
 
 	void deleteColumn( uint colnum ) {
-		foreach( inout row; mMapFile ) {
+
+		if( mWidth <= MIN_WIDTH ) return;
+
+		foreach( inout row; mCharMap ) {
 			row = row[0 .. colnum] ~ row[colnum+1 .. $];
 		}
 
 		if( x<mWidth && y<mHeight ) {
 			removeTile( x, y );
 			GameObjectFactory.create( mapCode, x*TILE_WIDTH, y*TILE_HEIGHT );
-			mMapFile[y][x]=mapCode;
+			mCharMap[y][x]=mapCode;
 		}
 	}
 
 				mMap[y][x].sub(o);
 				delete( o );
 			}
-			mMapFile[y][x]=' ';
+			mCharMap[y][x]=' ';
 		}
 	}
 }
                 mState = GAMEPART_STATE.MENU_OPTIONS;
                 break;
 			case 2:
-				mState = GAMEPART_STATE.MENU_MAP_EDITOR;
+				mState = GAMEPART_STATE.EDITOR; // .MENU_MAP_EDITOR;
 				break;
             case 3:
             case -1:
 }
 
 
+class MenuOptions : MenuBase {
+
+	string[] mOptions = ["Toggle Fulscreen","(Input configuration)", "Cancel" ];
+
+	this() {
+		mMenu.addItems( mOptions );
+	}
+
+	void onSelectedItem( int item ) {
+
+		if( item == mOptions.length-1 ) item = -1;
+
+		switch( item ) {
+			case -1:
+				mState = GAMEPART_STATE.MAIN_MENU;
+			break;
+
+			case 0:
+				gScreen.toggleFullscreen();
+			break;
+
+			case 1:
+				//mState = GAMEPART_STATE.INPUT_CONFIG;
+			break;
+
+			default: break;
+		}
+	}
+}
+

File src/screen.d

 
     SDL_Surface* mSurface;
     int mDelay;
-    uint mMilliseconds;
+    uint mMilliseconds;
+    bool mFullscreen;
 
     this() {
         gScreen = this;
 
     }
 
-
+	void toggleFullscreen() {
+		mFullscreen = !mFullscreen;
+		if( mFullscreen ) {
+			const uint videoflags = SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_FULLSCREEN;
+			mSurface = SDL_SetVideoMode(WIDTH, HEIGHT, 0, videoflags);
+		}
+		else {
+			const uint videoflags = SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_ANYFORMAT;
+			mSurface = SDL_SetVideoMode(WIDTH, HEIGHT, 0, videoflags);
+		}
+	}
 
 	void vsync() {