Commits

Anonymous committed 9627f06

* editeur quasi-complet (fonctions de base)
* refait les menus pour sélection à la souris ou au clavier ( utilisent la GUI )
* deplacement des maps dans maps/
* améliorations et debuggage GUI
* ajout fonction getKeyPressed() dans InputManager
* revu gestion du volume dans SoundManager
* revu gestion de l'icone dans Screen ( + ajout d'un icone )

  • Participants
  • Parent commits 13fb872

Comments (0)

Files changed (27)

 		</Unit>
 		<Unit filename="data/map.txt" />
 		<Unit filename="data/map2.txt" />
-		<Unit filename="data/map3.txt" />
 		<Unit filename="src/anim.d" />
 		<Unit filename="src/chrono.d" />
 		<Unit filename="src/configfile.d" />
 		<Unit filename="src/editor.d" />
 		<Unit filename="src/editoractions.d" />
+		<Unit filename="src/editordialogs.d" />
 		<Unit filename="src/editortools.d" />
 		<Unit filename="src/game.d" />
 		<Unit filename="src/gameconstants.d" />

data/delete.png

Added
New image

icon.bmp

Added
New image
+########################################
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#   X        @                         #
+# ===============                      #
+# ===============                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+########################################
+########################################
+#                                      #
+#  ##         ##                       #
+#  ##*       *##                       #
+#   #**     **#                        #
+#    #*#   #*#                         #
+#     ##   ##                          #
+#                                      #
+#  ##        ##                        #
+#  #=#      #=#                        #
+#   ########=#                         #
+#    #=#=#=##                          #
+#     ##=#=#                           #
+#      ####                            #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+########################################
+########################################################
+#      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#
+########################################################
+########################################################
+#                                                      =
+# X  ===== >>>>>>>>>>>>>>>>>>>>>>>#                    =
+#============================================= ======= #
+#                                                      #
+#                                                      #
+#                                                      #
+#                                                      #
+#  121212121212121212121                               #
+#==3434343434343u343434u==========                     #
+#                                =#################### #
+#                                    12121212121212121 1
+#    @                               34343434343434343 3
+# ==========                                        =  #
+#                                                   =  #
+#                     S                            c=  #
+#              =============                ========== #
+#                  ==========               =        = #
+#       &          =       u                         = #
+#,,,,,,,,,,,,,=                             =        = #
+#~~~~~~~~~~~~~=                                   &  = #
+#==============                       ================ #
+#                              ========              = #
+#                              =      =              = #
+#                           J  =   &  =              = #
+#                        ========== ===              = #
+#                 m          u                       = #
+# ,,,,,,,,,,,,,,,,=               ==================== #
+# ~~~~~~~~~~~~~~~~=               =            =       #
+# ~~~~~~~~~~~~~~~~=               =            =       #
+# ~~~~~~~~~~~~~~~~=  &         c        &&&   V  &&&&  #
+########################################################
+#################
+#     #####    =#
+#      ###     =#
+#       #      =#
+#       u      =#
+# @          X =#
+#===  J     ###=#
+#    =#=       =#
+#,,,,,,,,,,,,,,=#
+#~~~~~~~~~~~~~~=#
+#~~~~~~~~~~~~~~=#
+#===============#
+#################
+########################################
+#                                      #
+#                                      #
+#    #                                 #
+#   ##    ##                           #
+#   ## ##  ##                          #
+#   ## ##   ##                         #
+#   ##       ##                        #
+#   ##       ##                        #
+#   ##       ##                        #
+#   ## ##   ###                        #
+#   ## ##  ###                         #
+#   ##    ###                          #
+#    #    ##                           #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+########################################

maps/redcross.txt

+########################################
+#        **                            #
+#        **                            #
+#        **                            #
+#        **                            #
+#        **                            #
+#   ************                       #
+#   ************                       #
+#        **                            #
+#        **                            #
+#        **                            #
+#        **                            #
+#        **                            #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+#                                      #
+########################################
+####################
+#                  #
+#                  #
+#                  #
+#                  #
+#                  #
+                   
+#                  #
+#                  #
+#                  #
+#                  #
+#                  #
+#                  #
+#                  #
+####################
 
 input {
 
-		up { method = key 		keycode = 273 }
-		down { method = key 	keycode = 274 }
-		left { method = key		keycode = 276 }
-		RIGHT { method = key		keycode = 275 }
-		escape { method = key 	keycode = 27 }
-		crouch { method = key   keycode = 99  }  ; c
-		fire { method = key	keycode = 120 }  ; x
-		enter { method = key   keycode = 13 }  ; return
-		pause { method = key   keycode = 112 } ; p
-		volume_up { method = key  keycode = 280 }  ; page up
-		volume_down { method = key  keycode = 281 } ; page down
-		editor { method = key  keycode = 293 } ; F12
-
+		up			{ method = key	keycode = 273 }
+		down 		{ method = key	keycode = 274 }
+		left 		{ method = key	keycode = 276 }
+		RIGHT 		{ method = key	keycode = 275 }
+		escape 		{ method = key	keycode = 27  }
+		crouch 		{ method = key	keycode = 99  } ; c
+		fire 		{ method = key	keycode = 120 } ; x
+		enter 		{ method = key	keycode = 13  } ; return
+		pause 		{ method = key	keycode = 112 } ; p
+		volume_up 	{ method = key	keycode = 280 } ; page up
+		volume_down { method = key	keycode = 281 } ; page down
+		editor 		{ method = key  keycode = 293 } ; F12
 
 }
 
 import gui;
 import editoractions;
 import map;
+import editordialogs;
+
+debug import std.stdio;
 
 class Editor: public GamePart {
 
 	GAMEPART_STATE mState;
 	SDL_Surface* mBg;
-	SDL_Surface* mPointer;
+
 	float mVx = 0;
 	float mVy = 0;
 
 	ActionType mAction;
 	string mPointerIcon;
 	char mTileCode;
-
+	bool mInGame;
 	SDL_Rect mHighlightRect;
 	SDL_Surface* mHighlight;
+	MapNameDialog mMapNameDialog;
+	MapListDialog mMapListDialog;
+	Alert mAlert;
 
 	void delegate() fMode;
 	void delegate() fPreviousMode;
 
-	this() {
+	this( bool inGame=false ) {
 		SDL_WM_GrabInput(SDL_GRAB_ON);
 
+		mInGame = inGame;
+
 		gView.setTarget( null );
 
 		gView.setPosition( mVx,mVy );
 
 		mBg = gTextureManager.get( "background.png" );
-		mPointer = gTextureManager.get("pointer.png");
+
 		mAction = ActionType.NOTHING;
 
 		fMode = &editMode;
 	~this() {
 		SDL_WM_GrabInput(SDL_GRAB_OFF);
 		if( mTools !is null ) delete( mTools );
+		if( mHighlight != null ) SDL_FreeSurface( mHighlight );
 	}
 
 	void update() {
 			case ActionType.DELETE_TILE:
 			case ActionType.ADD_TILE:
 				highlight( xHL, yHL, gMap.TILE_WIDTH, gMap.TILE_HEIGHT );
+			break;
+
 			default:
 			break;
 		}
 				break;
 
 				case ActionType.ADD_TILE:
-					//gMap.addTile( xMap+xOffset, yMap+yOffset );
+					gMap.replaceTile( xMap+xOffset, yMap+yOffset, mTileCode );
+					gView.setTarget( null ); // to reset the target set in Hero.
 				break;
 
 				case ActionType.DELETE_TILE:
-					//gMap.removeTile( xMap+xOffset, yMap+yOffset );
+					gMap.removeTile( xMap+xOffset, yMap+yOffset );
 				break;
 
 				default:
 				break;
 			}
 		}
-
 	}
 
 	void moveMode() {
 
 		GUI.instance.update( x,y, gInputManager.isMouseButtonUp(SDL_BUTTON_LEFT) );
 
+		if( gInputManager.isMouseButtonUp(SDL_BUTTON_MIDDLE) ) {
+			fMode = &editMode;
+			return;
+		}
+
 		if( mTools.done() ) {
 			mAction = mTools.getAction();
-			mPointerIcon = mTools.getBitmap();
-			mTileCode = mTools.getCode();
-			GUI.instance.changePointer( mPointerIcon );
+
+
+			switch( mAction ) {
+				case ActionType.LOAD_MAP:
+					mMapListDialog = new MapListDialog();
+					fMode=&mapLoadMode;
+				break;
+
+				case ActionType.SAVE_MAP:
+					if( gMap.name != "" ) {
+						gMap.save();
+						fPreviousMode = &editMode;
+						fMode = &alertMode;
+						mAlert = new Alert("Map saved !");
+					}
+					else {
+						mMapNameDialog = new MapNameDialog();
+						fMode=&saveAsMode;
+					}
+				break;
+
+				case ActionType.TEST_MAP:
+					mAlert = new Alert("Not implemented yet !");
+					//fMode=&mapTestMode;
+					fMode = &alertMode;
+					fPreviousMode = &editMode;
+				break;
+
+				case ActionType.QUIT_EDITOR:
+					quit();
+				break;
+
+				default:
+					mPointerIcon = mTools.getBitmap();
+					GUI.instance.changePointer( mPointerIcon );
+					mTileCode = mTools.getCode();
+					fMode = &editMode;
+				break;
+			}
+
+			delete( mTools );
 		}
+	}
 
-		if( mTools.done() || gInputManager.isMouseButtonUp(SDL_BUTTON_MIDDLE) ) {
-			delete( mTools );
-			fMode = &editMode;
+	void checkQuit() {
+		if( mInGame && gInputManager.isReleased(IMA_TOGGLE_EDITOR) ) {
+			quit();
+		}
+	}
+
+
+	void quit() {
+		if( mInGame)
+			mState = GAMEPART_STATE.CONTINUE;
+		else
+			mState = GAMEPART_STATE.MAIN_MENU;
+	}
+
+	void saveAsMode() {
+
+		injectMouse();
+
+		if( mMapNameDialog.done() ) {
+			if( mMapNameDialog.ok && mMapNameDialog.text != "" ) {
+				gMap.name= std.string.strip(mMapNameDialog.text)~".txt";
+				if( gMap.save() ) {
+					mAlert = new Alert( "map '"~mMapNameDialog.text~"' saved !" );
+				}
+				else {
+					mAlert = new Alert( "Error while saving map" );
+				}
+				fMode = &alertMode;
+				fPreviousMode = &editMode;
+			}
+			else {
+				fMode = &editMode;
+			}
+			delete(mMapNameDialog);
 		}
 
 	}
 
-	void checkQuit() {
-		if( gInputManager.isReleased(IMA_TOGGLE_EDITOR)
-			|| gInputManager.isReleased(IMA_BACK))
-			mState = GAMEPART_STATE.CONTINUE;
+
+	void mapLoadMode() {
+		injectMouse();
+		if( mMapListDialog.done() ) {
+			if( mMapListDialog.ok() && mMapListDialog.getMapName().length > 0 ) {
+				delete(gMap);
+				new Map(mMapListDialog.getMapName() );
+				gView.setTarget( null );
+			}
+			fMode = &editMode;
+			delete( mMapListDialog );
+		}
+	}
+
+	void mapTestMode() {
+		fMode = &editMode;
+	}
+
+	void alertMode() {
+		injectMouse();
+		if( mAlert && mAlert.done() ) {
+			delete( mAlert );
+			fMode = fPreviousMode;
+		}
+	}
+
+	void injectMouse() {
+		int x,y;
+		gInputManager.getMousePositionRelative( x,y );
+		GUI.instance.update( x,y, gInputManager.isMouseButtonUp(SDL_BUTTON_LEFT) );
 	}
 
 
 			if( mHighlight != null ) SDL_FreeSurface( mHighlight );
 			mHighlight = SDL_CreateRGBSurface( SDL_HWSURFACE, w, h, 32, 0,0,0,0 );
 			SDL_FillRect( mHighlight, null, 0xFFFFFFFF );
-			SDL_SetAlpha( mHighlight, SDL_SRCALPHA, 64 );
+			SDL_SetAlpha( mHighlight, SDL_SRCALPHA, 128 );
 		}
 		mHighlightRect.x = cast(short)x;
 		mHighlightRect.y = cast(short)y;

src/editoractions.d

 	INSERT_COLUMN,
 	DELETE_COLUMN,
 	ADD_TILE,
-	DELETE_TILE
+	DELETE_TILE,
+	LOAD_MAP,
+	SAVE_MAP,
+	TEST_MAP,
+	QUIT_EDITOR
 };

src/editordialogs.d

+
+module editordialogs;
+
+import gui;
+import map;
+
+class Dialog {
+
+	GUI.Panel mPanel;
+	bool mDone;
+
+	this() {
+		mPanel = GUI.instance.addPanel(0,0);
+		mPanel.setAlign( GUI.Panel.ALIGN.CENTERED, GUI.Panel.ALIGN.CENTERED );
+	}
+
+	~this() {
+		GUI.instance.removePanel( mPanel );
+	}
+
+	bool done() { return mDone; }
+}
+
+
+class Alert: public Dialog, GUI.ButtonListener {
+	GUI.Button mOkButton;
+
+	this( string[] message ... ) {
+		super();
+		foreach( line; message ) {
+			mPanel.addLabel( line, "" );
+			mPanel.nl();
+		}
+		mPanel.nl();
+		mOkButton = mPanel.addTextButton( "OK", "", this );
+	}
+
+	void onButtonPressed( GUI.Button b ) {
+		mDone = true;
+	}
+}
+
+class MapNameDialog: public Dialog, GUI.ButtonListener {
+
+	GUI.TextInput mTextInput;
+	GUI.Button mOkButton;
+	GUI.Button mCancelButton;
+	bool mOk;
+
+	this() {
+		super();
+		mPanel.addLabel("Map name :" );
+		mPanel.nl();
+		mTextInput = mPanel.addTextInput( 200, "" );
+		mPanel.nl();
+		mPanel.nl();
+		mOkButton = mPanel.addTextButton( "OK", "", this );
+		mCancelButton = mPanel.addTextButton( "Cancel","", this );
+	}
+
+	bool ok() { return mOk; }
+
+	void onButtonPressed( GUI.Button b ) {
+		if( b == mOkButton ) {
+			mOk = true;
+			mDone = true;
+		}
+		if( b == mCancelButton ) {
+			mOk = false;
+			mDone = true;
+		}
+	}
+
+	string text() { return mTextInput.getText(); }
+}
+
+class MapListDialog: public Dialog, GUI.ButtonListener, GUI.MenuListener {
+
+	GUI.Button mCancelButton;
+	bool mOk;
+	GUI.Menu mMenu;
+	string mMapName;
+	MapList mMapList;
+
+	this() {
+		super();
+
+		mMapList = new MapList;
+
+		mPanel.addLabel( "Choose the map to load :" );
+		mPanel.nl();
+
+		mMenu = mPanel.addMenu( mMapList.getMenuText() );
+		mMenu.setListener(this);
+
+		mPanel.nl();
+
+		mCancelButton = mPanel.addTextButton( "Cancel","", this );
+	}
+
+	bool ok() { return mOk; }
+
+	void onSelectedItem( int item ) {
+		mDone = true;
+		if( item >= 0 ) {
+			mMapName = mMapList.getFileName(item);
+			mOk = true;
+		}
+	}
+
+	void onButtonPressed( GUI.Button b ) {
+		mDone = true;
+	}
+
+	string getMapName() {
+		return( mMapName );
+	}
+}
+
+

src/editortools.d

 
 		mActionsPanel = GUI.instance.addPanel( 0,0 );
 		foreach( inout action; mActions ) {
-			action.button = mActionsPanel.addTextButton( action.caption, "", this );
-			//mActionsPanel.nl();
+			if( action.type == ActionType.NOTHING )
+				mActionsPanel.nl();
+			else
+				action.button = mActionsPanel.addTextButton( action.caption, "", this );
 		}
 
 		initTiles();
 
-		mTilesPanel = GUI.instance.addPanel( 0, 50 );
+		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();
 	}
 
 	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( "", "", 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 );
+
 	}
 
 	void initTiles() {
 		mTiles ~= Tile( "greenkey.png", 'G' );
 		mTiles ~= Tile( "redkey.png", 'R' );
 		mTiles ~= Tile( "save_0.png", 'c' );
+		mTiles ~= Tile( "delete.png", '\0' );
 
 	}
 
 			if( tile.button == b ) {
 				mSelectedBitmap = tile.bitmap;
 				mSelectedCode = tile.mapCode;
-				mSelectedAction = ActionType.ADD_TILE;
+
+				if( tile.mapCode != '\0' )
+					mSelectedAction = ActionType.ADD_TILE;
+				else
+					mSelectedAction = ActionType.DELETE_TILE;
+
 				return;
 			}
 		}
 
 		foreach( action; mActions ) {
-			if( action.button == b ) {
+			if( action.type != ActionType.NOTHING && action.button == b ) {
 				mSelectedAction = action.type;
 				mSelectedBitmap = action.bitmap;
 				return;
         if( gInputManager.isReleased(IMA_VOLUME_UP)) gSoundManager.volumeUp();
         if( gInputManager.isReleased(IMA_VOLUME_DOWN)) gSoundManager.volumeDown();
 
-        if( gInputManager.isReleased(IMA_TOGGLE_EDITOR)) mState = GAMEPART_STATE.EDITOR;
+        if( gInputManager.isReleased(IMA_TOGGLE_EDITOR)) mState = GAMEPART_STATE.EDITOR_INGAME;
 
     }
 

src/gamemanager.d

 				mGamePart = new MainMenu();
 			break;
 
+			case GAMEPART_STATE.EDITOR_INGAME:
+				delete( mGamePart );
+				mGamePart = new Editor(true);
+			break;
+
 			case GAMEPART_STATE.EDITOR:
 				delete( mGamePart );
-				mGamePart = new Editor();
+				mGamePart = new Editor(false);
+			break;
+
+			case GAMEPART_STATE.MENU_MAP_EDITOR:
+				delete( mGamePart );
+				mGamePart = new MapMenuEditor();
 			break;
 
 			case GAMEPART_STATE.CONTINUE:

src/gameobjects/hero.d

 		mSoundMissile = gSoundManager.get("missile.ogg");
 		mSoundDead = gSoundManager.get("hero_dead.ogg");
 		mSoundRocket = gSoundManager.get("rocket.ogg");
-		mSoundJump = gSoundManager.get("jump.ogg");
+		mSoundJump = gSoundManager.get("jump.ogg");
+
+		update(); // avoid crash (in View.draw() ) if editor launched before game ( init mSurface )
 	}
 
 
 
 module gamepart;
 
-enum GAMEPART_STATE { GAME, GAME_WON, MAIN_MENU, EXIT, MENU_MAP, MENU_OPTIONS, NEW_GAME, UNPAUSE, EDITOR, CONTINUE };
+enum GAMEPART_STATE { GAME, GAME_WON, MAIN_MENU, EXIT, MENU_MAP,
+	MENU_MAP_EDITOR, MENU_OPTIONS, NEW_GAME, UNPAUSE, EDITOR, EDITOR_INGAME, CONTINUE };
 
 interface GamePart {
 
 import std.stdio;
 import textsurface;
 import slist;
-
+import inputmanager;
+import gameconstants;
 import std.stdio;
 
 class GUI {
 		int width() { return mW; }
 		int height() { return mH; }
 
-		/*
+/*
 		int derivedX() {
 			if( mParent !is null ) return (mX + mParent.derivedX());
 			else return( 0 );
 			if( mParent !is null ) return (mY + mParent.derivedY());
 			else return( 0 );
 		}
-		*/
+*/
 	}
 
 	interface Renderable {
 		void render();
 	}
 
+	class Label: public Widget, Renderable {
+		TextSurface mText;
+		this( Widget parent, int x, int y, string label, string fontid ) {
+			super(parent, x, y );
+			mText = new TextSurface(label); // , fontid, 0x00FFFF );
+			mW = mText.w;
+			mH = mText.h;
+		}
+
+		~this() {
+			delete(mText);
+		}
+
+		void render() {
+			mText.draw(mX,mY);
+		}
+
+		void move( int offx, int offy ) {
+			mX += offx;
+			mY += offy;
+		}
+	}
+
+	interface Typeable {
+		void injectKey( ubyte key );
+		string getText();
+	}
+
+	class TextInput: public Label, Typeable {
+		uint mWidth;
+		TextSurface mCaret;
+
+		this( Widget parent, int x, int y, uint width, string text="", string fontid="" ) {
+			super( parent, x, y, text, fontid );
+			mWidth = width;
+			mCaret = new TextSurface("|");
+		}
+
+		~this() {
+			delete( mCaret );
+		}
+
+		void injectKey( ubyte key ) {
+			if( key == 8 && mText.getText().length>0 ) {
+				mText.setText( mText.getText()[0 .. $-1] );
+				mW = mText.w;
+			}
+			if( key > 32 && key<127 && (mText.w < mWidth) ) {
+				mText.setText( mText.getText() ~ cast(char)key );
+				mW = mText.w;
+			}
+		}
+
+		string getText() { return( mText.getText() ); }
+
+		void render() {
+			super.render();
+			mCaret.draw( mX+mW, mY );
+		}
+	}
+
 	interface Clickable {
 		bool isIn(int x, int y);
 	}
 
 		ButtonListener mListener;
 		bool mHighlighted=false;
+		SDL_Surface* mHl;
+		SDL_Rect mRect;
 
 		this( Widget parent, int x, int y, ButtonListener listener ) {
 			super( parent, x, y );
 			mListener = listener;
 		}
 
+		~this() {
+			SDL_FreeSurface( mHl );
+		}
+
+		void buildHighlight( ubyte power=64 ) {
+			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 ) {
 			return( x>mX && x<mX+mW
 				&& y>mY && y<mY+mH );
 		}
 
 		void render() {
+			if( mHighlighted) SDL_BlitSurface( mHl, null, gScreen.mSurface, &mRect );
 		}
 
 		void notifyListener() {
 			mHighlighted = h;
 		}
 
+		void move( int offX, int offY ) {
+			mX += offX;
+			mY += offY;
+			mRect.x = cast(short)mX;
+			mRect.y = cast(short)mY;
+		}
+
 	}
 
 	interface ButtonListener {
 	class IconButton: public Button {
 
 		SDL_Surface* mIcon;
-		SDL_Rect mRect;
-		SDL_Surface* mHl;
 
 		this( Widget parent, int x, int y, string imageName, ButtonListener listener ) {
 			super( parent, x, y, listener );
 				x=cast(short)mX;
 				y=cast(short)mY;
 			}
-
-			mHl = SDL_CreateRGBSurface(
-				SDL_HWSURFACE,
-				mW, mH, 32,
-				0x00, 0x00, 0x00, 0x00 );
-			SDL_FillRect( mHl, null, 0xFFFFFFFF );
-			SDL_SetAlpha( mHl, SDL_SRCALPHA, 128 );
-
-		}
-
-		~this() {
-			SDL_FreeSurface( mHl );
+			buildHighlight(128);
 		}
 
 		void render() {
 			SDL_BlitSurface( mIcon, null, gScreen.mSurface, &mRect );
-			if( mHighlighted ) SDL_BlitSurface( mHl, null, gScreen.mSurface, &mRect );
+			super.render();
 		}
 
 	}
 
 	class TextButton: public Button {
-		SDL_Rect mRect;
-		SDL_Surface* mBg;
 		TextSurface mText;
 
 		this( Widget parent, int x, int y, string caption, string fontid, ButtonListener listener ) {
 
 			mText = new TextSurface(caption ); // , fontid, 0x00FFFF );
 
-			mW = mText.getSurface().w;
-			mH = mText.getSurface().h;
+			mW = mText.w;
+			mH = mText.h;
 			with( mRect ) {
 				x = cast(short)mX;
 				y = cast(short)mY;
 				w = cast(ushort)mW;
 				h = cast(ushort)mH;
 			}
-
-			mBg = SDL_CreateRGBSurface(
-				SDL_HWSURFACE,
-				mW, mH, 32,
-				0x00, 0x00, 0x00, 0x00 );
-			SDL_FillRect( mBg, null, 0xFFFFFFFF );
-			SDL_SetAlpha( mBg, SDL_SRCALPHA, 64 );
+			buildHighlight();
 		}
 
 		~this() {
 			delete( mText );
-			SDL_FreeSurface( mBg );
 		}
 
 		void render() {
-			SDL_BlitSurface( mBg, null, gScreen.mSurface, &mRect );
-			SDL_BlitSurface( mText.getSurface(), null, gScreen.mSurface, &mRect );
-			if( mHighlighted) SDL_BlitSurface( mBg, null, gScreen.mSurface, &mRect );
+			SDL_BlitSurface( mHl, null, gScreen.mSurface, &mRect );
+			//SDL_BlitSurface( mText.getSurface(), null, gScreen.mSurface, &mRect );
+			mText.draw(mX,mY);
+			super.render();
 		}
 	}
 
 	class Panel: public Widget, Renderable {
 
-		//Panel[] mPanels;
+		Panel[] mPanels;
 		Button[] mButtons;
+		Label[] mLabels;
+		TextInput[] mTextInputs;
 
 		int mMargin = 8;
 		int mLineHeight;
 		int mYInsert;
 		SDL_Surface* mBg;
 		SDL_Rect mRect;
+		Button mHighlighted;
+
+		enum ALIGN { FREE, MIN, MAX, CENTERED };
+		ALIGN mHAlign = ALIGN.FREE;
+		ALIGN mVAlign = ALIGN.FREE;
 
 		this( Widget parent, int x=0, int y=0 ) {
 			super( parent, x, y );
 		}
 
 		~this() {
-			foreach( b; mButtons ) {
-				delete(b);
-			}
-
 			if( mBg != null ) SDL_FreeSurface( mBg );
+			foreach( b; mButtons ) delete(b);
+			foreach( l; mLabels ) delete(l);
+			foreach( ti; mTextInputs ) delete(ti);
+			foreach( p; mPanels ) delete( p );
 		}
 
 		TextButton addTextButton( string caption, string fontid, ButtonListener listener=null ) {
 		IconButton addIconButton( string iconFileName, ButtonListener listener=null  ) {
 			IconButton b = new IconButton(this, mXInsert, mYInsert, iconFileName, listener );
 			mButtons ~= b;
-
 			resize( b.width, b.height );
 			return( b );
 		}
 
-/*
+		Label addLabel( string label, string fontid="" ) {
+			Label l = new Label( this, mXInsert, mYInsert, label, fontid );
+			mLabels ~= l;
+			resize( l.width, l.height );
+			return(l);
+		}
+
+		TextInput addTextInput( uint width, string text, string fontid="" ) {
+			TextInput ti = new TextInput( this, mXInsert, mYInsert, width, text, fontid );
+			mTextInputs ~= ti;
+			resize( ti.width, ti.height );
+			return(ti);
+		}
+
+
 		Panel addPanel() {
-			Panel p = new Panel( this );
+			Panel p = new Panel( this, mXInsert, mYInsert );
 			mPanels ~= p;
+			resize( p.width, p.height );
 			return( p );
 		}
-*/
+
+		Menu addMenu( string[] items ) {
+			Menu m = new Menu( this, mXInsert, mYInsert, items );
+			mPanels ~= m;
+			resize( m.width, m.height );
+			return( m );
+		}
+
 		void nl() {
 			mXInsert = mMargin;
 			mYInsert += mLineHeight + mLineGap;
 
 		private void resize( int w, int h ) {
 
-			adjustLineHeight( h );
+			bool updateBg = false;
+			if( mXInsert+w > mW-mMargin ) { mW = mXInsert + w + mMargin; updateBg = true; }
+			if( mYInsert+h > mH-mMargin ) { mH = mYInsert + h + mMargin; updateBg = true; }
 
 			mXInsert += ( w + mElementGap );
-			//mYInsert += h;
-			bool updateBg = false;
-			if( mXInsert+w > mW-mMargin ) { mW = mXInsert + mMargin; updateBg = true; }
-			if( mYInsert+h > mH-mMargin ) { mH = mYInsert + h + mMargin; updateBg = true; }
+			adjustLineHeight( h );
 
 			if( updateBg ) {
 				if( mBg != null ) SDL_FreeSurface( mBg );
 				mRect.h =cast(ushort)mH;
 			}
 
+			// special case for Panels : make the parent grow accordingly
+			//if( (cast(Panel)this) && (mParent !is null) ) (cast(Panel)mParent).resize( mW, mH );
+
+			move( mX, mY ); // move depending on auto-alignment
+
+		}
+
+		void move( int x, int y ) {
+			bool moved = false;
+
+			int prevX = mX;
+			int prevY = mY;
+
+			if( mHAlign == ALIGN.FREE ) mX = x; { moved = true; }
+			if( mVAlign == ALIGN.FREE ) mY = y; { moved = true; }
+
+			//moved = doAlign( mX, mHAlign, mW, gScreen.WIDTH ) || moved;
+			//moved = doAlign( mY, mVAlign, mH, gScreen.HEIGHT ) || moved;
+			doAlign( mX, mHAlign, mW, gScreen.WIDTH );
+			doAlign( mY, mVAlign, mH, gScreen.HEIGHT );
+
+			//if( moved ) {
+				mRect.x = cast(short)mX;
+				mRect.y = cast(short)mY;
+
+				int offX = mX - prevX;
+				int offY = mY - prevY;
+
+				foreach( inout b; mButtons ) {
+					b.move( offX, offY );
+				}
+				foreach( inout l; mLabels ) {
+					l.move( offX, offY );
+				}
+				foreach( inout ti; mTextInputs ) {
+					ti.move( offX, offY );
+				}
+				foreach( inout p; mPanels ) {
+					p.move( mX + offX, mY + offY );
+				}
+			//}
+		}
+
+		void setVerticalAlign( ALIGN vAlign ) {
+			mVAlign = vAlign;
+		}
+
+		void setHorizontalAlign( ALIGN hAlign ) {
+			mHAlign = hAlign;
+		}
+
+		void setAlign( ALIGN hAlign, ALIGN vAlign ) {
+			setHorizontalAlign( hAlign );
+			setVerticalAlign( vAlign );
+		}
+
+		private bool doAlign( inout int coord, ALIGN aAlign, int size, int max ) {
+
+			bool moved = false;
+
+			switch( aAlign ) {
+				case ALIGN.FREE:
+					moved = true;
+				break;
+
+				default:
+				break;
+
+				case ALIGN.MIN:
+					coord = 0;
+					moved = true;
+				break;
+
+				case ALIGN.MAX:
+					coord = max - size;
+					moved = true;
+				break;
+
+				case ALIGN.CENTERED:
+					coord = (max - size )/2;
+					moved = true;
+				break;
+			}
+			return( moved );
 		}
 
 		void render() {
 			if( mBg != null )
 				SDL_BlitSurface( mBg, null, gScreen.mSurface, &mRect );
 
+			foreach( b; mButtons ) { b.render(); }
+			foreach( l; mLabels ) { l.render(); }
+			foreach( ti; mTextInputs ) { ti.render(); }
+			foreach( p; mPanels ) { p.render(); }
+		}
+
+		void checkButtons( int x, int y, bool clicked ) {
+			foreach( p; mPanels ) {
+				p.checkButtons( x, y , clicked );
+			}
+
 			foreach( b; mButtons ) {
-				b.render();
+				if( b.isIn( x, y ) ) {
+					if( !b.highlighted())  {
+						b.highlight(true);
+						if( mHighlighted ) mHighlighted.highlight(false);
+						mHighlighted = b;
+					}
+					if( clicked ) b.notifyListener();
+				}
 			}
 		}
 
+		void injectKey( ubyte key ) {
+			foreach( ti; mTextInputs ) {
+				ti.injectKey( key );
+			}
+		}
+
+	}
+
+
+	interface MenuListener {
+		void onSelectedItem( int item);
+	}
+
+	class Menu: public Panel, ButtonListener {
+
+		TextButton[] mItems;
+		uint mSelectedItem;
+		MenuListener mListener;
+
+		this( Widget parent, int x=0, int y=0 ) {
+			super( parent, x, y );
+		}
+
+		this( Widget parent, int x, int y, string[] items ) {
+			super( parent, x, y);
+			addItems( items );
+		}
+
+		void addItems( string[] labels ... ) {
+			foreach( label; labels ) {
+				TextButton tb = this.addTextButton( label, "", this );
+				mItems ~= tb;
+				this.nl();
+			}
+		}
+
+		void setListener( MenuListener listener ) {
+			mListener = listener;
+		}
+
 		void checkButtons( int x, int y, bool clicked ) {
-			//foreach( p; mPanels ) {
-			//	p.checkButtons( x, y );
-			//}
-			foreach( b; mButtons ) {
-				if( b.isIn( x, y ) ) {
-					if( !b.highlighted()) b.highlight(true);
-					if( clicked ) b.notifyListener();
-				}
-				else {
-					if( b.highlighted()) b.highlight(false);
-				}
+			super.checkButtons(x,y,clicked);
+
+			if( gInputManager.isReleased( IMA_UP )) move( -1 );
+
+			if( gInputManager.isReleased( IMA_DOWN )) move( +1 );
+
+			if( gInputManager.isReleased( IMA_ACTION ))
+				if( mListener ) mListener.onSelectedItem( mSelectedItem );
+
+			if( gInputManager.isReleased( IMA_BACK ))
+				if( mListener ) mListener.onSelectedItem( -1 );
+
+		}
+
+		void move( int dir ) {
+			mSelectedItem = ( mSelectedItem + dir )%(mItems.length);
+			if( mHighlighted ) mHighlighted.highlight(false);
+			mItems[mSelectedItem].highlight(true);
+			mHighlighted = mItems[mSelectedItem];
+		}
+
+		void onButtonPressed( Button button ) {
+			foreach( i, b; mItems ) {
+				if( b == button )
+					if(mListener) mListener.onSelectedItem( i );
 			}
 		}
 	}
 
+
 	// =======================================
 
 	SDL_Surface* mPointer;
 
 	alias SList!(Panel) PanelList;
 	PanelList mPanels;
-
+//	SList!(Menu) mMenus;
 	static GUI mInstance;
 
 	public static GUI instance() {
 		mPanels = new PanelList;
 	}
 
+	public static void destroy() {
+		if( mInstance !is null ) delete( mInstance );
+	}
+
+	private ~this() {
+		if( mPointer != null ) SDL_FreeSurface( mPointer );
+	}
+
 	void showPointer() {
 		mPointerHidden = false;
 	}
 
 		foreach( p; mPanels ) {
 			p.checkButtons( mPx, mPy, click );
+			p.injectKey( gInputManager.getKeyUp() );
 			p.render();
 		}
 
 
 	}
 
-/*
-	bool isUp( bool pressed ) {
-		static bool wasPressed = false;
-
-		if (pressed)
-			wasPressed = true;
-		else {
-			if( wasPressed ) {
-				wasPressed = false;
-				return( true );
-			}
-		}
-		return( false );
-	}
-*/
-
 	Panel addPanel( int x, int y ) {
 		Panel p = new Panel( null, x, y );
 		//mPanels ~= p;
 
 	void removePanel( Panel p ) {
 		mPanels.sub( p );
-		delete(p);
+		delete( p );
 	}
 
+	Menu addMenu( int x=0, int y=0 ) {
+		Menu m = new Menu( null, x, y );
+		//mMenus.add(m);
+		mPanels.add( m );
+		return( m );
+	}
+/*
+	void removeMenu( Menu m ) {
+		mMenu.sub( m );
+		delete(m);
+	}
+*/
 }
 
 

src/inputmanager.d

 	ubyte* mSDLKeys;
 	// char[][] mPossibleActions;
 	char[] mConfigFilename;
+	ubyte mLastPressedKey;
 
 	bool[8] mMouseButtonsUp;
 	bool[8] mMouseButtonsDown;
 		mMouseButtonsUp[] = false;
 		mMouseButtonsDown[] = false;
 
+		mLastPressedKey = 0;
+
         while( SDL_PollEvent(&event) ) {
 
         	switch( event.type ) {
 					mMouseButtonsDown[event.button.button] = true;
 				break;
 
+				case SDL_KEYUP:
+					mLastPressedKey = cast(ubyte)event.key.keysym.sym;
+				break;
+
 				default:
 				break;
         	}
 		return( mSDLKeys );
 	}
 
+	ubyte getKeyUp() {
+		return( mLastPressedKey );
+	}
 }
 
 import std.stdio;
 import std.string;
 
-
 import gameobjectfactory;
 import gameobject;
 import objectlist;
+
+debug import std.stdio;
 
 public static Map gMap;
-public static MapList gMapList;
+//public static MapList gMapList;
 
 class MapList {
 
 	string[] mFileNames;
 
 	this() {
-		gMapList = this;
-		mFileNames = std.file.listdir( "data", RegExp(r"map*.\.txt$") );
+		mFileNames = std.file.listdir( "maps", RegExp(r".*.\.txt$") );
 		foreach( i, f; mFileNames ) {
-			// Remove "data/"
+			// Remove "maps/"
 			mFileNames[i] = f[5..$];
 		}
 		mMenuText = mFileNames;
 	}
 
-
 	string[] getMenuText() {
 		return mMenuText;
 	}
 
-
 	string getFileName(int map) {
 		return mFileNames[map];
 	}
-
 }
 
 class Map {
 
     this(string mapFileName) {
         gMap = this;
-
-		load( mapFileName );
+
+		if( mapFileName != "_new_" )
+			load( mapFileName );
+		else
+			genEmptyMap();
+
+		debug print();
+		build();
 
     }
 
+	debug {
+		void print() {
+			foreach( row; mMapFile ) {
+				writefln( row );
+			}
+		}
+	}
+
 	bool load( string mapFileName ) {
 
-		char[] file = cast(char[]) read("data/" ~ mapFileName);
+		char[] file = cast(char[]) read("maps/" ~ mapFileName);
 		mMapFileName = mapFileName;
 
         mMapFile = file.splitlines();
         mHeight = mMapFile.length;
         mWidth = mMapFile[0].length;
 
+
+		return( true );
+	}
+
+	bool save() {
+
+		char[] file;
+		foreach( line; mMapFile ) {
+			file ~= line ~ "\n";
+		}
+		try {
+			/*
+			if( std.file.exists( "maps" )==0 && std.file.isdir("maps") )
+				std.file.mkdir( "maps" );
+			*/
+			write( "maps/" ~ mMapFileName, file );
+		}
+		catch( FileException fe ) {
+			debug writefln( fe.msg );
+			return( false );
+		}
+
+		return(true);
+	}
+
+	void name( string filename ) {
+		mMapFileName = filename;
+	}
+
+	string name() {
+		return( mMapFileName );
+	}
+
+	void genEmptyMap() {
+		uint h = 20;
+		uint w = 40;
+
+		mMapFile ~= std.string.repeat( "#", w );
+		for( int i=0; i<=(h-2); i++ ) {
+			mMapFile ~= "#" ~ std.string.repeat( " ", w-2 ) ~ "#";
+		}
+		mMapFile ~= std.string.repeat( "#", w );
+
+		mHeight = mMapFile.length;
+		mWidth = mMapFile[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';
+
+		mMapFileName = "";
+	}
+
+	void build() {
 		// set map dimensions
 		mMap.length = mHeight;
 		for(int i = 0; i<mHeight; i++)
 			}
 
 		}
-		return( true );
-	}
+	}
 
-	bool save() {
-		return false;
-	}
 
 	/**
 	 * Returns the objects whose AABB overlap the given object's AABB.
 
 		mWidth--;
 	}
-
+
+
+	void replaceTile( uint x, uint y, char mapCode ) {
+		if( x<mWidth && y<mHeight ) {
+			removeTile( x, y );
+			GameObjectFactory.create( mapCode, x*TILE_WIDTH, y*TILE_HEIGHT );
+			mMapFile[y][x]=mapCode;
+		}
+	}
+
+	void removeTile( uint x, uint y ) {
+		if( x<mWidth  && y<mHeight ) {
+			foreach( inout o; mMap[y][x] ) {
+				mMap[y][x].sub(o);
+				delete( o );
+			}
+			mMapFile[y][x]=' ';
+		}
+	}
 }
 import screen;
 import textsurface;
 import texturemanager;
+import gui;
 
 // ============ base Menu class ===============
 
 class Menu : public GamePart {
 
 	TextSurface[2][] mText;
-	TextSurface mArrow;
-	int mCurrentItem = 0;
 	GAMEPART_STATE mState = GAMEPART_STATE.GAME;
 
-	this() {
-		mArrow = new TextSurface( "=>" );
-	}
-
 	~this() {
-		delete( mArrow );
 		foreach( t; mText ) {
 			if( t[0] !is null ) delete(t[0]);
 			if( t[1] !is null ) delete(t[1]);
     }
 
     // comportement de base
-    void processInput() {
-        if( gInputManager.isReleased( IMA_UP )) move(-1);
-        if( gInputManager.isReleased( IMA_DOWN )) move(1);
-        if( gInputManager.isReleased( IMA_ACTION )) action();
-    }
+    abstract void processInput();
 
 	void overlay( bool fade ) {
 		SDL_Surface* surf = SDL_CreateRGBSurface(
 			if( t[1] !is null ) {
 				t[1].draw( x+150, y );
 			}
-			if( i == mCurrentItem ) {
-				mArrow.draw( x-32, y );
-			}
 		}
 	}
 
     void action() {
     }
 
-	void move(int direction) {
-		mCurrentItem = (mCurrentItem+mText.length+direction) % mText.length;
-	}
-
     void addItem( string s ) {
     	TextSurface[2] t;
 		if( std.string.ifind( s, '|' ) != -1 ) {
 }
 
 // ============================================================
-
-// Les différents menus :
-
-class MainMenu : public Menu {
-
-    this() {
-        addItem("Play");
-        addItem("Options");
-        addItem("Exit");
-
-        overlay(true);
-    }
-
-    void action() {
-            switch( mCurrentItem ) {
-            case 0:
-                mState = GAMEPART_STATE.MENU_MAP;
-                break;
-            case 1:
-                mState = GAMEPART_STATE.MENU_OPTIONS;
-                break;
-            case 2:
-                mState = GAMEPART_STATE.EXIT;
-                break;
-            default:
-                break;
-        }
-    }
-
-    void processInput() {
-        Menu.processInput();
-        if( gInputManager.isReleased( IMA_BACK )) { mCurrentItem = 2; action(); }
-    }
-
-}
-
-
-class OptionsMenu : public Menu {
-    this() {
-        addItem("option 1");
-        addItem("option 2");
-        addItem("option 3");
-    }
-
-    void action() {
-        mState = GAMEPART_STATE.MAIN_MENU;
-    }
-}
-
-
 // Menu spécial utilisé par Game
 class PauseMenu : public Menu {
     this() {
     }
 }
 
-// Liste des maps
-class MapMenu : public Menu {
-    this() {
+// ============================================================
 
-        if( gMapList is null ) new MapList(); // on devrait vraiment avoir des singletons
+class MenuBase: GamePart, GUI.MenuListener {
+	GUI.Menu mMenu;
+	GAMEPART_STATE mState = GAMEPART_STATE.GAME;
+	SDL_Surface* mBg;
+	this() {
+		mMenu = GUI.instance.addMenu( 20, 20 );
+		mMenu.setListener(this);
+		mMenu.setAlign( GUI.Panel.ALIGN.CENTERED, GUI.Panel.ALIGN.CENTERED );
+		mBg = gTextureManager.get("background.png");
+	}
 
-        foreach( map; gMapList.getMenuText() ) {
-            addItem( map );
-        }
+	~this() {
+		GUI.instance.removePanel( mMenu );
+	}
+
+	void update() {
+
+		SDL_BlitSurface( mBg, null, gScreen.mSurface, null );
+
+		int x, y;
+		gInputManager.getMousePositionRelative( x,y );
+		GUI.instance.update( x,y, gInputManager.isMouseButtonUp(SDL_BUTTON_LEFT) );
+	}
+
+	abstract void onSelectedItem( int item );
+
+    GAMEPART_STATE getState() {
+        return( mState );
     }
 
-    void processInput() {
-        Menu.processInput();
-        if( gInputManager.isReleased( IMA_BACK )) { mState = GAMEPART_STATE.MAIN_MENU; }
-    }
+}
 
-    void action() {
-        if( gMap !is null ) delete(gMap);  // vraiment
-        new Map( gMapList.getFileName(mCurrentItem) );
-		mState = GAMEPART_STATE.NEW_GAME;
+class MainMenu: MenuBase {
+
+	this() {
+		mMenu.addItems( "Play", "Options", "Editor", "Quit" );
+	}
+
+	void onSelectedItem( int item ) {
+		switch( item ) {
+            case 0:
+                mState = GAMEPART_STATE.MENU_MAP;
+                break;
+            case 1:
+                mState = GAMEPART_STATE.MENU_OPTIONS;
+                break;
+			case 2:
+				mState = GAMEPART_STATE.MENU_MAP_EDITOR;
+				break;
+            case 3:
+            case -1:
+                mState = GAMEPART_STATE.EXIT;
+                break;
+            default:
+                break;
+        }
+	}
+}
+
+class MapMenu: MenuBase {
+
+	MapList mMapList;
+
+	this() {
+		mMapList = new MapList;
+		mMenu.addItems( mMapList.getMenuText() );
+	}
+
+	void onSelectedItem( int item ) {
+		if( item == -1 ) {
+			mState = GAMEPART_STATE.MAIN_MENU;
+		}
+		else {
+			if( gMap !is null ) delete(gMap);
+			new Map( mMapList.getFileName(item) );
+			mState = GAMEPART_STATE.NEW_GAME;
+		}
+	}
+}
+
+class MapMenuEditor: MenuBase {
+
+	MapList mMapList;
+
+	this() {
+		mMapList = new MapList;
+		string[] items;
+		items ~= "* New Map *";
+		items ~= mMapList.getMenuText();
+
+		mMenu.addItems( items );
+	}
+
+	void onSelectedItem( int item ) {
+		if( item == -1 ) {
+			mState = GAMEPART_STATE.MAIN_MENU;
+			return;
+		}
+		if( item == 0 ) {
+			if( gMap !is null ) delete(gMap);
+			new Map( "_new_" );
+			mState = GAMEPART_STATE.EDITOR;
+			return;
+		}
+
+		{
+			if( gMap !is null ) delete(gMap);
+			new Map( mMapList.getFileName(item-1) );
+			mState = GAMEPART_STATE.EDITOR;
+		}
     }
 }
+
+
         gScreen = this;
 
         DerelictSDL.load();
-
+
   		if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
 			throw new Error("Couldn't initialize SDL");
 		}
 
-		SDL_WM_SetCaption("RWKC", "data/Res1.ico");
+		SDL_WM_SetCaption("RWKC", "RWKC");
 		SDL_ShowCursor(SDL_DISABLE);
+
+		// must be called before SDL_SetVideoMode()
+		SDL_WM_SetIcon(SDL_LoadBMP("icon.bmp"), null );
 
 		const uint videoflags = SDL_HWSURFACE|SDL_DOUBLEBUF|SDL_ANYFORMAT;
 

src/soundmanager.d

 
 	void play( Mix_Chunk* sound, int volume = MIX_MAX_VOLUME ) {
 		int channel = Mix_PlayChannel( -1, sound, 0 /*loop*/ );
-		Mix_Volume( channel, volume );
+		Mix_Volume( channel, cast(int)(volume * (cast(float)mVolume/cast(float)MIX_MAX_VOLUME)) );
 	}
 
 }

src/textsurface.d

 	SDL_Surface* getSurface() {
 		return mSurface;
 	}
-
+
+	uint w() { if( mSurface) return mSurface.w;  else return(0); }
+	uint h() { if( mSurface) return mSurface.h; else return(0); }
 
 	void setText( char[] text ) {
 		if( text == "" ) {
 		mSurface = TTF_RenderUTF8_Blended( mFont, toStringz(text), fg );
 		assert( mSurface != null );
 	}
-
+
+	string getText() { return( mText ); }
 
 	void draw( int x, int y ) {
 		SDL_Rect rect = {