about summary refs log tree commit diff
path: root/src/game/editor
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/editor')
-rw-r--r--src/game/editor/auto_map.cpp202
-rw-r--r--src/game/editor/auto_map.h54
-rw-r--r--src/game/editor/editor.cpp (renamed from src/game/editor/ed_editor.cpp)1030
-rw-r--r--src/game/editor/editor.h (renamed from src/game/editor/ed_editor.h)94
-rw-r--r--src/game/editor/io.cpp (renamed from src/game/editor/ed_io.cpp)101
-rw-r--r--src/game/editor/layer_game.cpp (renamed from src/game/editor/ed_layer_game.cpp)4
-rw-r--r--src/game/editor/layer_quads.cpp (renamed from src/game/editor/ed_layer_quads.cpp)24
-rw-r--r--src/game/editor/layer_tiles.cpp (renamed from src/game/editor/ed_layer_tiles.cpp)61
-rw-r--r--src/game/editor/popups.cpp (renamed from src/game/editor/ed_popups.cpp)200
9 files changed, 1502 insertions, 268 deletions
diff --git a/src/game/editor/auto_map.cpp b/src/game/editor/auto_map.cpp
new file mode 100644
index 00000000..3abcf0f4
--- /dev/null
+++ b/src/game/editor/auto_map.cpp
@@ -0,0 +1,202 @@
+#include <stdio.h>	// sscanf
+
+#include <engine/console.h>
+#include <engine/storage.h>
+#include <engine/shared/linereader.h>
+
+#include "auto_map.h"
+#include "editor.h"
+
+CAutoMapper::CAutoMapper(CEditor *pEditor)
+{
+	m_pEditor = pEditor;
+	m_FileLoaded = false;
+}
+
+void CAutoMapper::Load(const char* pTileName)
+{
+	char aPath[256];
+	str_format(aPath, sizeof(aPath), "editor/%s.rules", pTileName);
+	IOHANDLE RulesFile = m_pEditor->Storage()->OpenFile(aPath, IOFLAG_READ, IStorage::TYPE_ALL);
+	if(!RulesFile)
+		return;
+
+	CLineReader LineReader;
+	LineReader.Init(RulesFile);
+
+	CConfiguration *pCurrentConf = 0;
+	CIndexRule *pCurrentIndex = 0;
+
+	char aBuf[256];
+
+	// read each line
+	while(char *pLine = LineReader.Get())
+	{
+		// skip blank/empty lines as well as comments
+		if(str_length(pLine) > 0 && pLine[0] != '#' && pLine[0] != '\n' && pLine[0] != '\r'
+			&& pLine[0] != '\t' && pLine[0] != '\v' && pLine[0] != ' ')
+		{
+			if(pLine[0]== '[')
+			{
+				// new configuration, get the name
+				pLine++;
+
+				CConfiguration NewConf;
+				int ID = m_lConfigs.add(NewConf);
+				pCurrentConf = &m_lConfigs[ID];
+
+				str_copy(pCurrentConf->m_aName, pLine, str_length(pLine));
+			}
+			else
+			{
+				if(!str_comp_num(pLine, "Index", 5))
+				{
+					// new index
+					int ID = 0;
+					char aFlip[128] = "";
+
+					sscanf(pLine, "Index %d %127s", &ID, aFlip);
+
+					CIndexRule NewIndexRule;
+					NewIndexRule.m_ID = ID;
+					NewIndexRule.m_Flag = 0;
+					NewIndexRule.m_RandomValue = 0;
+					NewIndexRule.m_BaseTile = false;
+
+					if(str_length(aFlip) > 0)
+					{
+						if(!str_comp(aFlip, "XFLIP"))
+							NewIndexRule.m_Flag = TILEFLAG_VFLIP;
+						else if(!str_comp(aFlip, "YFLIP"))
+							NewIndexRule.m_Flag = TILEFLAG_HFLIP;
+					}
+
+					// add the index rule object and make it current
+					int ArrayID = pCurrentConf->m_aIndexRules.add(NewIndexRule);
+					pCurrentIndex = &pCurrentConf->m_aIndexRules[ArrayID];
+				}
+				else if(!str_comp_num(pLine, "BaseTile", 8) && pCurrentIndex)
+				{
+					pCurrentIndex->m_BaseTile = true;
+				}
+				else if(!str_comp_num(pLine, "Pos", 3) && pCurrentIndex)
+				{
+					int x = 0, y = 0;
+					char aValue[128];
+					int Value = CPosRule::EMPTY;
+					bool IndexValue = false;
+
+					sscanf(pLine, "Pos %d %d %127s", &x, &y, aValue);
+
+					if(!str_comp(aValue, "FULL"))
+						Value = CPosRule::FULL;
+					else if(!str_comp_num(aValue, "INDEX", 5))
+					{
+						sscanf(pLine, "Pos %*d %*d INDEX %d", &Value);
+						IndexValue = true;
+					}
+
+					CPosRule NewPosRule = {x, y, Value, IndexValue};
+					pCurrentIndex->m_aRules.add(NewPosRule);
+				}
+				else if(!str_comp_num(pLine, "Random", 6) && pCurrentIndex)
+				{
+					sscanf(pLine, "Random %d", &pCurrentIndex->m_RandomValue);
+				}
+			}
+		}
+	}
+
+	io_close(RulesFile);
+
+	str_format(aBuf, sizeof(aBuf),"loaded %s", aPath);
+	m_pEditor->Console()->Print(IConsole::OUTPUT_LEVEL_DEBUG, "editor", aBuf);
+
+	m_FileLoaded = true;
+}
+
+const char* CAutoMapper::GetConfigName(int Index)
+{
+	if(Index < 0 || Index >= m_lConfigs.size())
+		return "";
+
+	return m_lConfigs[Index].m_aName;
+}
+
+void CAutoMapper::Proceed(CLayerTiles *pLayer, int ConfigID)
+{
+	if(!m_FileLoaded || pLayer->m_Readonly || ConfigID < 0 || ConfigID >= m_lConfigs.size())
+		return;
+
+	CConfiguration *pConf = &m_lConfigs[ConfigID];
+
+	if(!pConf->m_aIndexRules.size())
+		return;
+
+	int BaseTile = 1;
+
+	// find base tile if there is one
+	for(int i = 0; i < pConf->m_aIndexRules.size(); ++i)
+	{
+		if(pConf->m_aIndexRules[i].m_BaseTile)
+		{
+			BaseTile = pConf->m_aIndexRules[i].m_ID;
+			break;
+		}
+	}
+
+	// auto map !
+	int MaxIndex = pLayer->m_Width*pLayer->m_Height;
+	for(int y = 0; y < pLayer->m_Height; y++)
+		for(int x = 0; x < pLayer->m_Width; x++)
+		{
+			CTile *pTile = &(pLayer->m_pTiles[y*pLayer->m_Width+x]);
+			if(pTile->m_Index == 0)
+				continue;
+
+			pTile->m_Index = BaseTile;
+			m_pEditor->m_Map.m_Modified = true;
+
+			if(y == 0 || y == pLayer->m_Height-1 || x == 0 || x == pLayer->m_Width-1)
+				continue;
+
+			for(int i = 0; i < pConf->m_aIndexRules.size(); ++i)
+			{
+				if(pConf->m_aIndexRules[i].m_BaseTile)
+					continue;
+
+				bool RespectRules = true;
+				for(int j = 0; j < pConf->m_aIndexRules[i].m_aRules.size() && RespectRules; ++j)
+				{
+					CPosRule *pRule = &pConf->m_aIndexRules[i].m_aRules[j];
+					int CheckIndex = (y+pRule->m_Y)*pLayer->m_Width+(x+pRule->m_X);
+
+					if(CheckIndex < 0 || CheckIndex >= MaxIndex)
+						RespectRules = false;
+					else
+					{
+ 						if(pRule->m_IndexValue)
+						{
+							if(pLayer->m_pTiles[CheckIndex].m_Index != pRule->m_Value)
+								RespectRules = false;
+						}
+						else
+						{
+							if(pLayer->m_pTiles[CheckIndex].m_Index > 0 && pRule->m_Value == CPosRule::EMPTY)
+								RespectRules = false;
+
+							if(pLayer->m_pTiles[CheckIndex].m_Index == 0 && pRule->m_Value == CPosRule::FULL)
+								RespectRules = false;
+						}
+					}
+				}
+
+				if(RespectRules &&
+					(pConf->m_aIndexRules[i].m_RandomValue <= 1 || (int)((float)rand() / ((float)RAND_MAX + 1) * pConf->m_aIndexRules[i].m_RandomValue) == 1))
+				{
+					pTile->m_Index = pConf->m_aIndexRules[i].m_ID;
+					pTile->m_Flags = pConf->m_aIndexRules[i].m_Flag;
+				}
+			}
+		}
+}
diff --git a/src/game/editor/auto_map.h b/src/game/editor/auto_map.h
new file mode 100644
index 00000000..c5537d4f
--- /dev/null
+++ b/src/game/editor/auto_map.h
@@ -0,0 +1,54 @@
+#ifndef GAME_EDITOR_AUTO_MAP_H
+#define GAME_EDITOR_AUTO_MAP_H
+
+#include <base/tl/array.h>
+
+class CAutoMapper
+{
+	struct CPosRule
+	{
+		int m_X;
+		int m_Y;
+		int m_Value;
+		bool m_IndexValue;
+
+		enum
+		{
+			EMPTY=0,
+			FULL
+		};
+	};
+
+	struct CIndexRule
+	{
+		int m_ID;
+		array<CPosRule> m_aRules;
+		int m_Flag;
+		int m_RandomValue;
+		bool m_BaseTile;
+	};
+
+	struct CConfiguration
+	{
+		array<CIndexRule> m_aIndexRules;
+		char m_aName[128];
+	};
+
+public:
+	CAutoMapper(class CEditor *pEditor);
+
+	void Load(const char* pTileName);
+	void Proceed(class CLayerTiles *pLayer, int ConfigID);
+
+	int ConfigNamesNum() { return m_lConfigs.size(); }
+	const char* GetConfigName(int Index);
+
+	const bool IsLoaded() { return m_FileLoaded; }
+private:
+	array<CConfiguration> m_lConfigs;
+	class CEditor *m_pEditor;
+	bool m_FileLoaded;
+};
+
+
+#endif
diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/editor.cpp
index 1163dc4a..fa1024e0 100644
--- a/src/game/editor/ed_editor.cpp
+++ b/src/game/editor/editor.cpp
@@ -8,20 +8,20 @@
 #include <engine/client.h>
 #include <engine/console.h>
 #include <engine/graphics.h>
-#include <engine/textrender.h>
 #include <engine/input.h>
 #include <engine/keys.h>
 #include <engine/storage.h>
+#include <engine/textrender.h>
 
-#include <game/client/ui.h>
 #include <game/gamecore.h>
+#include <game/localization.h>
+#include <game/client/lineinput.h>
 #include <game/client/render.h>
+#include <game/client/ui.h>
 #include <game/generated/client_data.h>
 
-#include "ed_editor.h"
-#include <game/client/lineinput.h>
-
-#include <game/localization.h>
+#include "auto_map.h"
+#include "editor.h"
 
 int CEditor::ms_CheckerTexture;
 int CEditor::ms_BackgroundTexture;
@@ -41,9 +41,10 @@ CEditorImage::~CEditorImage()
 
 CLayerGroup::CLayerGroup()
 {
-	m_pName = "";
+	m_aName[0] = 0;
 	m_Visible = true;
 	m_SaveToMap = true;
+	m_Collapse = false;
 	m_GameGroup = false;
 	m_OffsetX = 0;
 	m_OffsetY = 0;
@@ -163,7 +164,7 @@ void CEditorImage::AnalyseTileFlags()
 	int tw = m_Width/16; // tilesizes
 	int th = m_Height/16;
 	if ( tw == th )
-    {
+	{
 		unsigned char *pPixelData = (unsigned char *)m_pData;
 
 		int TileID = 0;
@@ -189,39 +190,84 @@ void CEditorImage::AnalyseTileFlags()
 
 }
 
+void CEditor::EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser)
+{
+	CEditor *pThis = (CEditor *)pUser;
+	if(Env < 0 || Env >= pThis->m_Map.m_lEnvelopes.size())
+	{
+		pChannels[0] = 0;
+		pChannels[1] = 0;
+		pChannels[2] = 0;
+		pChannels[3] = 0;
+		return;
+	}
+
+	CEnvelope *e = pThis->m_Map.m_lEnvelopes[Env];
+	float t = pThis->m_AnimateTime+TimeOffset;
+	t *= pThis->m_AnimateSpeed;
+	e->Eval(t, pChannels);
+}
+
 /********************************************************
  OTHER
 *********************************************************/
 
 // copied from gc_menu.cpp, should be more generalized
 //extern int ui_do_edit_box(void *id, const CUIRect *rect, char *str, int str_size, float font_size, bool hidden=false);
-
-int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden)
+int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden, int Corners)
 {
-    int Inside = UI()->MouseInside(pRect);
+	int Inside = UI()->MouseInside(pRect);
 	bool ReturnValue = false;
+	bool UpdateOffset = false;
 	static int s_AtIndex = 0;
+	static bool s_DoScroll = false;
+	static float s_ScrollStart = 0.0f;
+
+	FontSize *= UI()->Scale();
 
 	if(UI()->LastActiveItem() == pID)
 	{
+		m_EditBoxActive = 2;
 		int Len = str_length(pStr);
+		if(Len == 0)
+			s_AtIndex = 0;
 
 		if(Inside && UI()->MouseButton(0))
 		{
+			s_DoScroll = true;
+			s_ScrollStart = UI()->MouseX();
 			int MxRel = (int)(UI()->MouseX() - pRect->x);
 
-			for (int i = 1; i <= Len; i++)
+			for(int i = 1; i <= Len; i++)
 			{
-				if (TextRender()->TextWidth(0, FontSize, pStr, i) + 10 > MxRel)
+				if(TextRender()->TextWidth(0, FontSize, pStr, i) - *Offset > MxRel)
 				{
 					s_AtIndex = i - 1;
 					break;
 				}
 
-				if (i == Len)
+				if(i == Len)
 					s_AtIndex = Len;
 			}
 		}
+		else if(!UI()->MouseButton(0))
+			s_DoScroll = false;
+		else if(s_DoScroll)
+		{
+			// do scrolling
+			if(UI()->MouseX() < pRect->x && s_ScrollStart-UI()->MouseX() > 10.0f)
+			{
+				s_AtIndex = max(0, s_AtIndex-1);
+				s_ScrollStart = UI()->MouseX();
+				UpdateOffset = true;
+			}
+			else if(UI()->MouseX() > pRect->x+pRect->w && UI()->MouseX()-s_ScrollStart > 10.0f)
+			{
+				s_AtIndex = min(Len, s_AtIndex+1);
+				s_ScrollStart = UI()->MouseX();
+				UpdateOffset = true;
+			}
+		}
 
 		for(int i = 0; i < Input()->NumEvents(); i++)
 		{
@@ -235,7 +281,11 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str
 	if(UI()->ActiveItem() == pID)
 	{
 		if(!UI()->MouseButton(0))
+		{
+			s_AtIndex = min(s_AtIndex, str_length(pStr));
+			s_DoScroll = false;
 			UI()->SetActiveItem(0);
+		}
 	}
 	else if(UI()->HotItem() == pID)
 	{
@@ -251,8 +301,8 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str
 		UI()->SetHotItem(pID);
 
 	CUIRect Textbox = *pRect;
-	RenderTools()->DrawUIRect(&Textbox, vec4(1,1,1,0.5f), CUI::CORNER_ALL, 3.0f);
-	Textbox.VMargin(3.0f, &Textbox);
+	RenderTools()->DrawUIRect(&Textbox, vec4(1, 1, 1, 0.5f), Corners, 3.0f);
+	Textbox.VMargin(2.0f, &Textbox);
 
 	const char *pDisplayStr = pStr;
 	char aStars[128];
@@ -268,19 +318,47 @@ int CEditor::DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned Str
 		pDisplayStr = aStars;
 	}
 
+	// check if the text has to be moved
+	if(UI()->LastActiveItem() == pID && !JustGotActive && (UpdateOffset || Input()->NumEvents()))
+	{
+		float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
+		if(w-*Offset > Textbox.w)
+		{
+			// move to the left
+			float wt = TextRender()->TextWidth(0, FontSize, pDisplayStr, -1);
+			do
+			{
+				*Offset += min(wt-*Offset-Textbox.w, Textbox.w/3);
+			}
+			while(w-*Offset > Textbox.w);
+		}
+		else if(w-*Offset < 0.0f)
+		{
+			// move to the right
+			do
+			{
+				*Offset = max(0.0f, *Offset-Textbox.w/3);
+			}
+			while(w-*Offset < 0.0f);
+		}
+	}
+	UI()->ClipEnable(pRect);
+	Textbox.x -= *Offset;
+
 	UI()->DoLabel(&Textbox, pDisplayStr, FontSize, -1);
-	
-	//TODO: make it blink
+
+	// render the cursor
 	if(UI()->LastActiveItem() == pID && !JustGotActive)
 	{
 		float w = TextRender()->TextWidth(0, FontSize, pDisplayStr, s_AtIndex);
 		Textbox = *pRect;
 		Textbox.VSplitLeft(2.0f, 0, &Textbox);
-		Textbox.x += w*UI()->Scale();
-		Textbox.y -= FontSize/10.f;
-		
-		UI()->DoLabel(&Textbox, "|", FontSize*1.1f, -1);
+		Textbox.x += (w-*Offset-TextRender()->TextWidth(0, FontSize, "|", -1)/2);
+
+		if((2*time_get()/time_freq()) % 2)	// make it blink
+			UI()->DoLabel(&Textbox, "|", FontSize, -1);
 	}
+	UI()->ClipDisable();
 
 	return ReturnValue;
 }
@@ -303,8 +381,8 @@ float CEditor::UiDoScrollbarV(const void *pID, const CUIRect *pRect, float Curre
 	Handle.y += (pRect->h-Handle.h)*Current;
 
 	// logic
-    float Ret = Current;
-    int Inside = UI()->MouseInside(&Handle);
+	float Ret = Current;
+	int Inside = UI()->MouseInside(&Handle);
 
 	if(UI()->ActiveItem() == pID)
 	{
@@ -345,7 +423,7 @@ float CEditor::UiDoScrollbarV(const void *pID, const CUIRect *pRect, float Curre
 	Slider.Margin(5.0f, &Slider);
 	RenderTools()->DrawUIRect(&Slider, vec4(1,1,1,0.25f)*ButtonColorMul(pID), CUI::CORNER_ALL, 2.5f);
 
-    return Ret;
+	return Ret;
 }
 
 vec4 CEditor::GetButtonColor(const void *pID, int Checked)
@@ -412,7 +490,7 @@ int CEditor::DoButton_File(const void *pID, const char *pText, int Checked, cons
 int CEditor::DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
 {
 	CUIRect r = *pRect;
-    RenderTools()->DrawUIRect(&r, vec4(0.5f, 0.5f, 0.5f, 1.0f), CUI::CORNER_T, 3.0f);
+	RenderTools()->DrawUIRect(&r, vec4(0.5f, 0.5f, 0.5f, 1.0f), CUI::CORNER_T, 3.0f);
 
 	r = *pRect;
 	r.VMargin(5.0f, &r);
@@ -437,18 +515,18 @@ int CEditor::DoButton_MenuItem(const void *pID, const char *pText, int Checked,
 int CEditor::DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip)
 {
 	RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), CUI::CORNER_T, 5.0f);
-    CUIRect NewRect = *pRect;
-    NewRect.y += NewRect.h/2.0f-7.0f;
-    UI()->DoLabel(&NewRect, pText, 10, 0, -1);
+	CUIRect NewRect = *pRect;
+	NewRect.y += NewRect.h/2.0f-7.0f;
+	UI()->DoLabel(&NewRect, pText, 10, 0, -1);
 	return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
 }
 
-int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners)
+int CEditor::DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize)
 {
 	RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, Checked), Corners, 3.0f);
-    CUIRect NewRect = *pRect;
-    NewRect.y += NewRect.h/2.0f-7.0f;
-    UI()->DoLabel(&NewRect, pText, 10, 0, -1);
+	CUIRect NewRect = *pRect;
+	NewRect.HMargin(NewRect.h/2.0f-FontSize/2.0f-1.0f, &NewRect);
+	UI()->DoLabel(&NewRect, pText, FontSize, 0, -1);
 	return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
 }
 
@@ -466,6 +544,49 @@ int CEditor::DoButton_ButtonDec(const void *pID, const char *pText, int Checked,
 	return DoButton_Editor_Common(pID, pText, Checked, pRect, Flags, pToolTip);
 }
 
+void CEditor::RenderGrid(CLayerGroup *pGroup)
+{
+	if(!m_GridActive)
+		return;
+
+	float aGroupPoints[4];
+	pGroup->Mapping(aGroupPoints);
+
+	float w = UI()->Screen()->w;
+	float h = UI()->Screen()->h;
+
+	int LineDistance = GetLineDistance();
+
+	int XOffset = aGroupPoints[0]/LineDistance;
+	int YOffset = aGroupPoints[1]/LineDistance;
+	int XGridOffset = XOffset % m_GridFactor;
+	int YGridOffset = YOffset % m_GridFactor;
+
+	Graphics()->TextureSet(-1);
+	Graphics()->LinesBegin();
+
+	for(int i = 0; i < (int)w; i++)
+	{
+		if((i+YGridOffset) % m_GridFactor == 0)
+			Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f);
+		else
+			Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f);
+
+		IGraphics::CLineItem Line = IGraphics::CLineItem(LineDistance*XOffset, LineDistance*i+LineDistance*YOffset, w+aGroupPoints[2], LineDistance*i+LineDistance*YOffset);
+		Graphics()->LinesDraw(&Line, 1);
+
+		if((i+XGridOffset) % m_GridFactor == 0)
+			Graphics()->SetColor(1.0f, 0.3f, 0.3f, 0.3f);
+		else
+			Graphics()->SetColor(1.0f, 1.0f, 1.0f, 0.15f);
+
+		Line = IGraphics::CLineItem(LineDistance*i+LineDistance*XOffset, LineDistance*YOffset, LineDistance*i+LineDistance*XOffset, h+aGroupPoints[3]);
+		Graphics()->LinesDraw(&Line, 1);
+	}
+	Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
+	Graphics()->LinesEnd();
+}
+
 void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brightness)
 {
 	Graphics()->TextureSet(Texture);
@@ -480,17 +601,14 @@ void CEditor::RenderBackground(CUIRect View, int Texture, float Size, float Brig
 
 int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, int Current, int Min, int Max, int Step, float Scale, const char *pToolTip)
 {
-    // logic
-    static float s_Value;
-    int Ret = 0;
-    int Inside = UI()->MouseInside(pRect);
+	// logic
+	static float s_Value;
+	int Inside = UI()->MouseInside(pRect);
 
 	if(UI()->ActiveItem() == pID)
 	{
 		if(!UI()->MouseButton(0))
 		{
-			if(Inside)
-				Ret = 1;
 			m_LockMouse = false;
 			UI()->SetActiveItem(0);
 		}
@@ -534,9 +652,9 @@ int CEditor::UiDoValueSelector(void *pID, CUIRect *pRect, const char *pLabel, in
 	char aBuf[128];
 	str_format(aBuf, sizeof(aBuf),"%s %d", pLabel, Current);
 	RenderTools()->DrawUIRect(pRect, GetButtonColor(pID, 0), CUI::CORNER_ALL, 5.0f);
-    pRect->y += pRect->h/2.0f-7.0f;
-    UI()->DoLabel(pRect, aBuf, 10, 0, -1);
-    
+	pRect->y += pRect->h/2.0f-7.0f;
+	UI()->DoLabel(pRect, aBuf, 10, 0, -1);
+
 	return Current;
 }
 
@@ -595,7 +713,7 @@ void CEditor::CallbackAppendMap(const char *pFileName, int StorageType, void *pU
 		pEditor->m_aFileName[0] = 0;
 	else
 		pEditor->SortImages();
-	
+
 	pEditor->m_Dialog = DIALOG_NONE;
 }
 void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUser)
@@ -616,7 +734,7 @@ void CEditor::CallbackSaveMap(const char *pFileName, int StorageType, void *pUse
 		pEditor->m_ValidSaveFilename = StorageType == IStorage::TYPE_SAVE && pEditor->m_pFileDialogPath == pEditor->m_aFileDialogCurrentFolder;
 		pEditor->m_Map.m_Modified = false;
 	}
-	
+
 	pEditor->m_Dialog = DIALOG_NONE;
 }
 
@@ -624,32 +742,38 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 {
 	CUIRect TB_Top, TB_Bottom;
 	CUIRect Button;
-	
+
 	ToolBar.HSplitTop(ToolBar.h/2.0f, &TB_Top, &TB_Bottom);
-	
-    TB_Top.HSplitBottom(2.5f, &TB_Top, 0);
-    TB_Bottom.HSplitTop(2.5f, 0, &TB_Bottom);
+
+	TB_Top.HSplitBottom(2.5f, &TB_Top, 0);
+	TB_Bottom.HSplitTop(2.5f, 0, &TB_Bottom);
 
 	// ctrl+o to open
-	if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))
+	if(Input()->KeyDown('o') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) && m_Dialog == DIALOG_NONE)
 	{
 		if(HasUnsavedData())
 		{
-			m_PopupEventType = POPEVENT_LOAD;
-			m_PopupEventActivated = true;
+			if(!m_PopupEventWasActivated)
+			{
+				m_PopupEventType = POPEVENT_LOAD;
+				m_PopupEventActivated = true;
+			}
 		}
 		else
 			InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_MAP, "Load map", "Load", "maps", "", CallbackOpenMap, this);
 	}
 
 	// ctrl+s to save
-	if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)))
+	if(Input()->KeyDown('s') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL)) && m_Dialog == DIALOG_NONE)
 	{
-		if(m_aFileName[0] && m_ValidSaveFilename)	
+		if(m_aFileName[0] && m_ValidSaveFilename)
 		{
-			str_copy(m_aFileSaveName, m_aFileName, sizeof(m_aFileSaveName));
-			m_PopupEventType = POPEVENT_SAVE;
-			m_PopupEventActivated = true;
+			if(!m_PopupEventWasActivated)
+			{
+				str_copy(m_aFileSaveName, m_aFileName, sizeof(m_aFileSaveName));
+				m_PopupEventType = POPEVENT_SAVE;
+				m_PopupEventActivated = true;
+			}
 		}
 		else
 			InvokeFileDialog(IStorage::TYPE_SAVE, FILETYPE_MAP, "Save map", "Save", "maps", "", CallbackSaveMap, this);
@@ -696,6 +820,7 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 		(Input()->KeyDown('i') && (Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))))
 	{
 		m_ShowTileInfo = !m_ShowTileInfo;
+		m_ShowEnvelopePreview = 0;
 	}
 
 	TB_Top.VSplitLeft(15.0f, 0, &TB_Top);
@@ -703,12 +828,12 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 	// zoom group
 	TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
 	static int s_ZoomOutButton = 0;
-	if(DoButton_Ex(&s_ZoomOutButton, "ZO", 0, &Button, 0, "[NumPad-] Zoom out", CUI::CORNER_L) || Input()->KeyDown(KEY_KP_MINUS))
+	if(DoButton_Ex(&s_ZoomOutButton, "ZO", 0, &Button, 0, "[NumPad-] Zoom out", CUI::CORNER_L))
 		m_ZoomLevel += 50;
 
 	TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
 	static int s_ZoomNormalButton = 0;
-	if(DoButton_Ex(&s_ZoomNormalButton, "1:1", 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", 0) || Input()->KeyDown(KEY_KP_MULTIPLY))
+	if(DoButton_Ex(&s_ZoomNormalButton, "1:1", 0, &Button, 0, "[NumPad*] Zoom to normal and remove editor offset", 0))
 	{
 		m_EditorOffsetX = 0;
 		m_EditorOffsetY = 0;
@@ -717,7 +842,7 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 
 	TB_Top.VSplitLeft(30.0f, &Button, &TB_Top);
 	static int s_ZoomInButton = 0;
-	if(DoButton_Ex(&s_ZoomInButton, "ZI", 0, &Button, 0, "[NumPad+] Zoom in", CUI::CORNER_R) || Input()->KeyDown(KEY_KP_PLUS))
+	if(DoButton_Ex(&s_ZoomInButton, "ZI", 0, &Button, 0, "[NumPad+] Zoom in", CUI::CORNER_R))
 		m_ZoomLevel -= 50;
 
 	TB_Top.VSplitLeft(10.0f, 0, &TB_Top);
@@ -741,16 +866,6 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 			m_AnimateSpeed -= 0.5f;
 	}
 
-	if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP) && m_Dialog == DIALOG_NONE)
-		m_ZoomLevel -= 20;
-
-	if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN) && m_Dialog == DIALOG_NONE)
-		m_ZoomLevel += 20;
-
-	if(m_ZoomLevel < 50)
-		m_ZoomLevel = 50;
-	m_WorldZoom = m_ZoomLevel/100.0f;
-
 	TB_Top.VSplitLeft(10.0f, &Button, &TB_Top);
 
 
@@ -837,17 +952,17 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 			}
 		}
 	}
-    
+
 	// tile manipulation
 	{
 		TB_Bottom.VSplitLeft(40.0f, &Button, &TB_Bottom);
 		static int s_BorderBut = 0;
 		CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
-		
+
 		if(DoButton_Editor(&s_BorderBut, "Border", pT?0:-1, &Button, 0, "Adds border tiles"))
 		{
 			if(pT)
-                DoMapBorder();
+				DoMapBorder();
 		}
 	}
 
@@ -856,11 +971,46 @@ void CEditor::DoToolbar(CUIRect ToolBar)
 	// refocus button
 	TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom);
 	static int s_RefocusButton = 0;
-	if(DoButton_Editor(&s_RefocusButton, "Refocus", m_WorldOffsetX&&m_WorldOffsetY?0:-1, &Button, 0, "[HOME] Restore map focus") || Input()->KeyDown(KEY_HOME))
+	if(DoButton_Editor(&s_RefocusButton, "Refocus", m_WorldOffsetX&&m_WorldOffsetY?0:-1, &Button, 0, "[HOME] Restore map focus") || (m_EditBoxActive == 0 && Input()->KeyDown(KEY_HOME)))
 	{
 		m_WorldOffsetX = 0;
 		m_WorldOffsetY = 0;
 	}
+
+	TB_Bottom.VSplitLeft(5.0f, 0, &TB_Bottom);
+
+	// grid button
+	TB_Bottom.VSplitLeft(50.0f, &Button, &TB_Bottom);
+	static int s_GridButton = 0;
+	if(DoButton_Editor(&s_GridButton, "Grid", m_GridActive, &Button, 0, "Toggle Grid"))
+	{
+		m_GridActive = !m_GridActive;
+	}
+
+	TB_Bottom.VSplitLeft(30.0f, 0, &TB_Bottom);
+
+	// grid zoom
+	TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
+	static int s_GridIncreaseButton = 0;
+	if(DoButton_Ex(&s_GridIncreaseButton, "G-", 0, &Button, 0, "Decrease grid", CUI::CORNER_L))
+	{
+		if(m_GridFactor > 1)
+			m_GridFactor--;
+	}
+
+	TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
+	static int s_GridNormalButton = 0;
+	if(DoButton_Ex(&s_GridNormalButton, "1", 0, &Button, 0, "Normal grid", 0))
+		m_GridFactor = 1;
+
+	TB_Bottom.VSplitLeft(30.0f, &Button, &TB_Bottom);
+
+	static int s_GridDecreaseButton = 0;
+	if(DoButton_Ex(&s_GridDecreaseButton, "G+", 0, &Button, 0, "Increase grid", CUI::CORNER_R))
+	{
+		if(m_GridFactor < 15)
+			m_GridFactor++;
+	}
 }
 
 static void Rotate(CPoint *pCenter, CPoint *pPoint, float Rotation)
@@ -901,6 +1051,12 @@ void CEditor::DoQuad(CQuad *q, int Index)
 	if(dx*dx+dy*dy < 50)
 		UI()->SetHotItem(pID);
 
+	bool IgnoreGrid;
+	if(Input()->KeyPressed(KEY_LALT) || Input()->KeyPressed(KEY_RALT))
+		IgnoreGrid = true;
+	else
+		IgnoreGrid = false;
+
 	// draw selection background
 	if(m_SelectedQuad == Index)
 	{
@@ -911,27 +1067,82 @@ void CEditor::DoQuad(CQuad *q, int Index)
 
 	if(UI()->ActiveItem() == pID)
 	{
-		// check if we only should move pivot
-		if(s_Operation == OP_MOVE_PIVOT)
-		{
-			q->m_aPoints[4].x += f2fx(wx-s_LastWx);
-			q->m_aPoints[4].y += f2fx(wy-s_LastWy);
-		}
-		else if(s_Operation == OP_MOVE_ALL)
+		if(m_MouseDeltaWx*m_MouseDeltaWx+m_MouseDeltaWy*m_MouseDeltaWy > 0.5f)
 		{
-			// move all points including pivot
-			for(int v = 0; v < 5; v++)
+			// check if we only should move pivot
+			if(s_Operation == OP_MOVE_PIVOT)
 			{
-				q->m_aPoints[v].x += f2fx(wx-s_LastWx);
-				q->m_aPoints[v].y += f2fx(wy-s_LastWy);
+				if(m_GridActive && !IgnoreGrid)
+				{
+					int LineDistance = GetLineDistance();
+
+					float x = 0.0f;
+					float y = 0.0f;
+					if(wx >= 0)
+						x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+					else
+						x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+					if(wy >= 0)
+						y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+					else
+						y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+
+					q->m_aPoints[4].x = f2fx(x);
+					q->m_aPoints[4].y = f2fx(y);
+				}
+				else
+				{
+					q->m_aPoints[4].x += f2fx(wx-s_LastWx);
+					q->m_aPoints[4].y += f2fx(wy-s_LastWy);
+				}
 			}
-		}
-		else if(s_Operation == OP_ROTATE)
-		{
-			for(int v = 0; v < 4; v++)
+			else if(s_Operation == OP_MOVE_ALL)
+			{
+				// move all points including pivot
+				if(m_GridActive && !IgnoreGrid)
+				{
+					int LineDistance = GetLineDistance();
+
+					float x = 0.0f;
+					float y = 0.0f;
+					if(wx >= 0)
+						x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+					else
+						x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+					if(wy >= 0)
+						y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+					else
+						y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+
+					int OldX = q->m_aPoints[4].x;
+					int OldY = q->m_aPoints[4].y;
+					q->m_aPoints[4].x = f2fx(x);
+					q->m_aPoints[4].y = f2fx(y);
+					int DiffX = q->m_aPoints[4].x - OldX;
+					int DiffY = q->m_aPoints[4].y - OldY;
+
+					for(int v = 0; v < 4; v++)
+					{
+						q->m_aPoints[v].x += DiffX;
+						q->m_aPoints[v].y += DiffY;
+					}
+				}
+				else
+				{
+					for(int v = 0; v < 5; v++)
+					{
+							q->m_aPoints[v].x += f2fx(wx-s_LastWx);
+							q->m_aPoints[v].y += f2fx(wy-s_LastWy);
+					}
+				}
+			}
+			else if(s_Operation == OP_ROTATE)
 			{
-				q->m_aPoints[v] = s_RotatePoints[v];
-				Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle);
+				for(int v = 0; v < 4; v++)
+				{
+					q->m_aPoints[v] = s_RotatePoints[v];
+					Rotate(&q->m_aPoints[4], &q->m_aPoints[v], s_RotateAngle);
+				}
 			}
 		}
 
@@ -967,7 +1178,7 @@ void CEditor::DoQuad(CQuad *q, int Index)
 		ms_pUiGotContext = pID;
 
 		Graphics()->SetColor(1,1,1,1);
-		m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate.";
+		m_pTooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate. Hold alt to ignore grid.";
 
 		if(UI()->MouseButton(0))
 		{
@@ -987,6 +1198,8 @@ void CEditor::DoQuad(CQuad *q, int Index)
 				s_Operation = OP_MOVE_ALL;
 
 			UI()->SetActiveItem(pID);
+			if(m_SelectedQuad != Index)
+				m_SelectedPoints = 0;
 			m_SelectedQuad = Index;
 			s_LastWx = wx;
 			s_LastWy = wy;
@@ -994,6 +1207,8 @@ void CEditor::DoQuad(CQuad *q, int Index)
 
 		if(UI()->MouseButton(1))
 		{
+			if(m_SelectedQuad != Index)
+				m_SelectedPoints = 0;
 			m_SelectedQuad = Index;
 			s_Operation = OP_CONTEXT_MENU;
 			UI()->SetActiveItem(pID);
@@ -1040,6 +1255,12 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
 	static bool s_Moved;
 	static int s_Operation = OP_NONE;
 
+	bool IgnoreGrid;
+	if(Input()->KeyPressed(KEY_LALT) || Input()->KeyPressed(KEY_RALT))
+		IgnoreGrid = true;
+	else
+		IgnoreGrid = false;
+
 	if(UI()->ActiveItem() == pID)
 	{
 		float dx = m_MouseDeltaWx;
@@ -1054,24 +1275,49 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
 		{
 			if(s_Operation == OP_MOVEPOINT)
 			{
-				for(int m = 0; m < 4; m++)
-					if(m_SelectedPoints&(1<<m))
-					{
-						pQuad->m_aPoints[m].x += f2fx(dx);
-						pQuad->m_aPoints[m].y += f2fx(dy);
-					}
+				if(m_GridActive && !IgnoreGrid)
+				{
+					for(int m = 0; m < 4; m++)
+						if(m_SelectedPoints&(1<<m))
+						{
+							int LineDistance = GetLineDistance();
+
+							float x = 0.0f;
+							float y = 0.0f;
+							if(wx >= 0)
+								x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+							else
+								x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+							if(wy >= 0)
+								y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+							else
+								y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+
+							pQuad->m_aPoints[m].x = f2fx(x);
+							pQuad->m_aPoints[m].y = f2fx(y);
+						}
+				}
+				else
+				{
+					for(int m = 0; m < 4; m++)
+						if(m_SelectedPoints&(1<<m))
+						{
+							pQuad->m_aPoints[m].x += f2fx(dx);
+							pQuad->m_aPoints[m].y += f2fx(dy);
+						}
+				}
 			}
 			else if(s_Operation == OP_MOVEUV)
 			{
 				for(int m = 0; m < 4; m++)
 					if(m_SelectedPoints&(1<<m))
 					{
-						// 0,2;1,3 - line x 
+						// 0,2;1,3 - line x
 						// 0,1;2,3 - line y
 
 						pQuad->m_aTexcoords[m].x += f2fx(dx*0.001f);
 						pQuad->m_aTexcoords[(m+2)%4].x += f2fx(dx*0.001f);
-						
+
 						pQuad->m_aTexcoords[m].y += f2fx(dy*0.001f);
 						pQuad->m_aTexcoords[m^1].y += f2fx(dy*0.001f);
 					}
@@ -1110,7 +1356,7 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
 		ms_pUiGotContext = pID;
 
 		Graphics()->SetColor(1,1,1,1);
-		m_pTooltip = "Left mouse button to move. Hold shift to move the texture.";
+		m_pTooltip = "Left mouse button to move. Hold shift to move the texture. Hold alt to ignore grid.";
 
 		if(UI()->MouseButton(0))
 		{
@@ -1157,14 +1403,222 @@ void CEditor::DoQuadPoint(CQuad *pQuad, int QuadIndex, int V)
 	Graphics()->QuadsDraw(&QuadItem, 1);
 }
 
-void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
+void CEditor::DoQuadEnvelopes(CQuad *pQuad, int Index, int TexID)
 {
-	//UI()->ClipEnable(&view);
+	CEnvelope *pEnvelope = 0x0;
+	if(pQuad->m_PosEnv >= 0 && pQuad->m_PosEnv < m_Map.m_lEnvelopes.size())
+		pEnvelope = m_Map.m_lEnvelopes[pQuad->m_PosEnv];
+	if (!pEnvelope)
+		return;
+
+	//QuadParams
+	CPoint *pPoints = pQuad->m_aPoints;
+
+	//Draw Lines
+	Graphics()->TextureSet(-1);
+	Graphics()->LinesBegin();
+		Graphics()->SetColor(80.0f/255, 150.0f/255, 230.f/255, 0.5f);
+		for(int i = 0; i < pEnvelope->m_lPoints.size()-1; i++)
+		{
+			float OffsetX =  fx2f(pEnvelope->m_lPoints[i].m_aValues[0]);
+			float OffsetY = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]);
+			vec2 Pos0 = vec2(fx2f(pPoints[4].x)+OffsetX, fx2f(pPoints[4].y)+OffsetY);
 
-	bool ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE;
+			OffsetX = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[0]);
+			OffsetY = fx2f(pEnvelope->m_lPoints[i+1].m_aValues[1]);
+			vec2 Pos1 = vec2(fx2f(pPoints[4].x)+OffsetX, fx2f(pPoints[4].y)+OffsetY);
+
+			IGraphics::CLineItem Line = IGraphics::CLineItem(Pos0.x, Pos0.y, Pos1.x, Pos1.y);
+			Graphics()->LinesDraw(&Line, 1);
+		}
+		Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
+	Graphics()->LinesEnd();
+
+	//Draw Quads
+	for(int i = 0; i < pEnvelope->m_lPoints.size(); i++)
+	{
+		Graphics()->TextureSet(TexID);
+		Graphics()->QuadsBegin();
+		
+		//Calc Env Position
+		float OffsetX =  fx2f(pEnvelope->m_lPoints[i].m_aValues[0]);
+		float OffsetY = fx2f(pEnvelope->m_lPoints[i].m_aValues[1]);
+		float Rot = fx2f(pEnvelope->m_lPoints[i].m_aValues[2])/360.0f*pi*2;
+
+		//Set Colours
+		float Alpha = (m_SelectedQuadEnvelope == pQuad->m_PosEnv && m_SelectedEnvelopePoint == i) ? 0.65f : 0.35f;
+		IGraphics::CColorVertex aArray[4] = {
+			IGraphics::CColorVertex(0, pQuad->m_aColors[0].r, pQuad->m_aColors[0].g, pQuad->m_aColors[0].b, Alpha),
+			IGraphics::CColorVertex(1, pQuad->m_aColors[1].r, pQuad->m_aColors[1].g, pQuad->m_aColors[1].b, Alpha),
+			IGraphics::CColorVertex(2, pQuad->m_aColors[2].r, pQuad->m_aColors[2].g, pQuad->m_aColors[2].b, Alpha),
+			IGraphics::CColorVertex(3, pQuad->m_aColors[3].r, pQuad->m_aColors[3].g, pQuad->m_aColors[3].b, Alpha)};
+		Graphics()->SetColorVertex(aArray, 4);
+
+		//Rotation
+		if(Rot != 0)
+		{
+			static CPoint aRotated[4];
+			aRotated[0] = pQuad->m_aPoints[0];
+			aRotated[1] = pQuad->m_aPoints[1];
+			aRotated[2] = pQuad->m_aPoints[2];
+			aRotated[3] = pQuad->m_aPoints[3];
+			pPoints = aRotated;
+
+			Rotate(&pQuad->m_aPoints[4], &aRotated[0], Rot);
+			Rotate(&pQuad->m_aPoints[4], &aRotated[1], Rot);
+			Rotate(&pQuad->m_aPoints[4], &aRotated[2], Rot);
+			Rotate(&pQuad->m_aPoints[4], &aRotated[3], Rot);
+		}
+
+		//Set Texture Coords
+		Graphics()->QuadsSetSubsetFree(
+			fx2f(pQuad->m_aTexcoords[0].x), fx2f(pQuad->m_aTexcoords[0].y),
+			fx2f(pQuad->m_aTexcoords[1].x), fx2f(pQuad->m_aTexcoords[1].y),
+			fx2f(pQuad->m_aTexcoords[2].x), fx2f(pQuad->m_aTexcoords[2].y),
+			fx2f(pQuad->m_aTexcoords[3].x), fx2f(pQuad->m_aTexcoords[3].y)
+		);
+
+		//Set Quad Coords & Draw
+		IGraphics::CFreeformItem Freeform(
+			fx2f(pPoints[0].x)+OffsetX, fx2f(pPoints[0].y)+OffsetY,
+			fx2f(pPoints[1].x)+OffsetX, fx2f(pPoints[1].y)+OffsetY,
+			fx2f(pPoints[2].x)+OffsetX, fx2f(pPoints[2].y)+OffsetY,
+			fx2f(pPoints[3].x)+OffsetX, fx2f(pPoints[3].y)+OffsetY);
+		Graphics()->QuadsDrawFreeform(&Freeform, 1);
+
+		Graphics()->QuadsEnd();
+		
+		Graphics()->TextureSet(-1);
+		Graphics()->QuadsBegin();
+		DoQuadEnvPoint(pQuad, Index, i);
+		Graphics()->QuadsEnd();
+	}
+}
+
+void CEditor::DoQuadEnvPoint(CQuad *pQuad, int QIndex, int PIndex)
+{
+	enum
+	{
+		OP_NONE=0,
+		OP_MOVE,
+		OP_ROTATE,
+	};
 
+	// some basic values
+	static float s_LastWx;
+	static float s_LastWy;
+	static int s_Operation = OP_NONE;
+	float wx = UI()->MouseWorldX();
+	float wy = UI()->MouseWorldY();
+	CEnvelope *pEnvelope = m_Map.m_lEnvelopes[pQuad->m_PosEnv];
+	void *pID = &pEnvelope->m_lPoints[PIndex];
+	static int s_ActQIndex = -1;
+
+	// get pivot
+	float CenterX = fx2f(pQuad->m_aPoints[4].x)+fx2f(pEnvelope->m_lPoints[PIndex].m_aValues[0]);
+	float CenterY = fx2f(pQuad->m_aPoints[4].y)+fx2f(pEnvelope->m_lPoints[PIndex].m_aValues[1]);
+
+	float dx = (CenterX - wx)/m_WorldZoom;
+	float dy = (CenterY - wy)/m_WorldZoom;
+	if(dx*dx+dy*dy < 50.0f && UI()->ActiveItem() == 0)
+	{
+		UI()->SetHotItem(pID);
+		s_ActQIndex = QIndex;
+	}
+
+	bool IgnoreGrid;
+	if(Input()->KeyPressed(KEY_LALT) || Input()->KeyPressed(KEY_RALT))
+		IgnoreGrid = true;
+	else
+		IgnoreGrid = false;
+
+	if(UI()->ActiveItem() == pID && s_ActQIndex == QIndex)
+	{
+		if(s_Operation == OP_MOVE)
+		{
+			if(m_GridActive && !IgnoreGrid)
+			{
+				int LineDistance = GetLineDistance();
+
+				float x = 0.0f;
+				float y = 0.0f;
+				if(wx >= 0)
+					x = (int)((wx+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+				else
+					x = (int)((wx-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+				if(wy >= 0)
+					y = (int)((wy+(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+				else
+					y = (int)((wy-(LineDistance/2)*m_GridFactor)/(LineDistance*m_GridFactor)) * (LineDistance*m_GridFactor);
+
+				pEnvelope->m_lPoints[PIndex].m_aValues[0] = f2fx(x);
+				pEnvelope->m_lPoints[PIndex].m_aValues[1] = f2fx(y);
+			}
+			else
+			{
+				pEnvelope->m_lPoints[PIndex].m_aValues[0] += f2fx(wx-s_LastWx);
+				pEnvelope->m_lPoints[PIndex].m_aValues[1] += f2fx(wy-s_LastWy);
+			}
+		}
+		else if(s_Operation == OP_ROTATE)
+			pEnvelope->m_lPoints[PIndex].m_aValues[2] += 10*m_MouseDeltaX;
+
+		s_LastWx = wx;
+		s_LastWy = wy;
+
+		if(!UI()->MouseButton(0))
+		{
+			m_LockMouse = false;
+			s_Operation = OP_NONE;
+			UI()->SetActiveItem(0);
+		}
+
+		Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
+	}
+	else if(UI()->HotItem() == pID && s_ActQIndex == QIndex)
+	{
+		ms_pUiGotContext = pID;
+
+		Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
+		m_pTooltip = "Left mouse button to move. Hold ctrl to rotate. Hold alt to ignore grid.";
+
+		if(UI()->MouseButton(0))
+		{
+			if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL))
+			{
+				m_LockMouse = true;
+				s_Operation = OP_ROTATE;
+			}
+			else
+				s_Operation = OP_MOVE;
+
+			m_SelectedEnvelopePoint = PIndex;
+			m_SelectedQuadEnvelope = pQuad->m_PosEnv;
+
+			UI()->SetActiveItem(pID);
+			if(m_SelectedQuad != QIndex)
+				m_SelectedPoints = 0;
+			m_SelectedQuad = QIndex;
+			s_LastWx = wx;
+			s_LastWy = wy;
+		}
+		else
+		{
+			m_SelectedEnvelopePoint = -1;
+			m_SelectedQuadEnvelope = -1;
+		}
+	}
+	else
+		Graphics()->SetColor(0.0f, 1.0f, 0.0f, 1.0f);
+
+	IGraphics::CQuadItem QuadItem(CenterX, CenterY, 5.0f*m_WorldZoom, 5.0f*m_WorldZoom);
+	Graphics()->QuadsDraw(&QuadItem, 1);
+}
+
+void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
+{
 	// render all good stuff
-	if(!ShowPicker)
+	if(!m_ShowPicker)
 	{
 		for(int g = 0; g < m_Map.m_lGroups.size(); g++)
 		{
@@ -1182,7 +1636,16 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 
 		CLayerTiles *pT = static_cast<CLayerTiles *>(GetSelectedLayerType(0, LAYERTYPE_TILES));
 		if(m_ShowTileInfo && pT && pT->m_Visible && m_ZoomLevel <= 300)
+		{
+			GetSelectedGroup()->MapScreen();
 			pT->ShowInfo();
+		}
+	}
+	else
+	{
+		// fix aspect ratio of the image in the picker
+		float Max = min(View.w, View.h);
+		View.w = View.h = Max;
 	}
 
 	static void *s_pEditorID = (void *)&s_pEditorID;
@@ -1196,8 +1659,6 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 
 	static float s_StartWx = 0;
 	static float s_StartWy = 0;
-	static float s_StartMx = 0;
-	static float s_StartMy = 0;
 
 	enum
 	{
@@ -1210,7 +1671,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 	};
 
 	// remap the screen so it can display the whole tileset
-	if(ShowPicker)
+	if(m_ShowPicker)
 	{
 		CUIRect Screen = *UI()->Screen();
 		float Size = 32.0*16.0f;
@@ -1239,7 +1700,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 	int NumEditLayers = 0;
 	NumEditLayers = 0;
 
-	if(ShowPicker)
+	if(m_ShowPicker)
 	{
 		pEditLayers[0] = &m_TilesetPicker;
 		NumEditLayers++;
@@ -1255,6 +1716,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 		{
 			g->MapScreen();
 
+			RenderGrid(g);
+
 			for(int i = 0; i < NumEditLayers; i++)
 			{
 				if(pEditLayers[i]->m_Type != LAYERTYPE_TILES)
@@ -1285,8 +1748,6 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 		{
 			s_StartWx = wx;
 			s_StartWy = wy;
-			s_StartMx = mx;
-			s_StartMy = my;
 
 			if(Input()->KeyPressed(KEY_LCTRL) || Input()->KeyPressed(KEY_RCTRL) || UI()->MouseButton(2))
 			{
@@ -1365,8 +1826,8 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 				{
 					if(!UI()->MouseButton(0))
 					{
-                        for(int k = 0; k < NumEditLayers; k++)
-                            pEditLayers[k]->FillSelection(m_Brush.IsEmpty(), m_Brush.m_lLayers[0], r);
+						for(int k = 0; k < NumEditLayers; k++)
+							pEditLayers[k]->FillSelection(m_Brush.IsEmpty(), m_Brush.m_lLayers[0], r);
 					}
 					else
 					{
@@ -1398,10 +1859,10 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 						}
 
 					}
-					
+
 					CLayerTiles *pLayer = (CLayerTiles*)GetSelectedLayerType(0, LAYERTYPE_TILES);
 					if((Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT)) && pLayer)
-                        s_Operation = OP_BRUSH_PAINT;
+						s_Operation = OP_BRUSH_PAINT;
 				}
 
 				if(!m_Brush.IsEmpty())
@@ -1445,7 +1906,7 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 
 		// quad editing
 		{
-			if(!ShowPicker && m_Brush.IsEmpty())
+			if(!m_ShowPicker && m_Brush.IsEmpty())
 			{
 				// fetch layers
 				CLayerGroup *g = GetSelectedGroup();
@@ -1458,6 +1919,9 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 					{
 						CLayerQuads *pLayer = (CLayerQuads *)pEditLayers[k];
 
+						if(!m_ShowEnvelopePreview)
+							m_ShowEnvelopePreview = 2;
+
 						Graphics()->TextureSet(-1);
 						Graphics()->QuadsBegin();
 						for(int i = 0; i < pLayer->m_lQuads.size(); i++)
@@ -1507,11 +1971,11 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 		}
 	}
 
-	if(GetSelectedGroup() && GetSelectedGroup()->m_UseClipping)
+	if(!m_ShowPicker && GetSelectedGroup() && GetSelectedGroup()->m_UseClipping)
 	{
 		CLayerGroup *g = m_Map.m_pGameGroup;
 		g->MapScreen();
-	
+
 		Graphics()->TextureSet(-1);
 		Graphics()->LinesBegin();
 
@@ -1615,6 +2079,24 @@ void CEditor::DoMapEditor(CUIRect View, CUIRect ToolBar)
 		Graphics()->LinesEnd();
 	}
 
+	if (!m_ShowPicker && m_ShowTileInfo && m_ShowEnvelopePreview != 0 && GetSelectedLayer(0) && GetSelectedLayer(0)->m_Type == LAYERTYPE_QUADS)
+	{
+		GetSelectedGroup()->MapScreen();
+
+		CLayerQuads *pLayer = (CLayerQuads*)GetSelectedLayer(0);
+		int TexID = -1;
+		if(pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size())
+			TexID = m_Map.m_lImages[pLayer->m_Image]->m_TexID;
+
+		for(int i = 0; i < pLayer->m_lQuads.size(); i++)
+		{
+			if((m_ShowEnvelopePreview == 1 && pLayer->m_lQuads[i].m_PosEnv == m_SelectedEnvelope) || m_ShowEnvelopePreview == 2)
+				DoQuadEnvelopes(&pLayer->m_lQuads[i], i, TexID);
+		}
+
+		m_ShowEnvelopePreview = 0;
+    }
+
 	Graphics()->MapScreen(UI()->Screen()->x, UI()->Screen()->y, UI()->Screen()->w, UI()->Screen()->h);
 	//UI()->ClipDisable();
 }
@@ -1710,7 +2192,7 @@ int CEditor::DoProperties(CUIRect *pToolBox, CProperty *pProps, int *pIDs, int *
 			if(pProps[i].m_Value < 0)
 				str_copy(aBuf, "None", sizeof(aBuf));
 			else
-				str_format(aBuf, sizeof(aBuf),"%s",  m_Map.m_lImages[pProps[i].m_Value]->m_aName);
+				str_format(aBuf, sizeof(aBuf),"%s", m_Map.m_lImages[pProps[i].m_Value]->m_aName);
 
 			if(DoButton_Editor(&pIDs[i], aBuf, 0, &Shifter, 0, 0))
 				PopupSelectImageInvoke(pProps[i].m_Value, UI()->MouseX(), UI()->MouseY());
@@ -1772,22 +2254,18 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 	CUIRect Slot, Button;
 	char aBuf[64];
 
-	int ValidGroup = 0;
-	int ValidLayer = 0;
-	if(m_SelectedGroup >= 0 && m_SelectedGroup < m_Map.m_lGroups.size())
-		ValidGroup = 1;
-
-	if(ValidGroup && m_SelectedLayer >= 0 && m_SelectedLayer < m_Map.m_lGroups[m_SelectedGroup]->m_lLayers.size())
-		ValidLayer = 1;
-
 	float LayersHeight = 12.0f;	 // Height of AddGroup button
 	static int s_ScrollBar = 0;
 	static float s_ScrollValue = 0;
 
 	for(int g = 0; g < m_Map.m_lGroups.size(); g++)
+	{
 		// Each group is 19.0f
 		// Each layer is 14.0f
-		LayersHeight += 19.0f + m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f;
+		LayersHeight += 19.0f;
+		if(!m_Map.m_lGroups[g]->m_Collapse)
+			LayersHeight += m_Map.m_lGroups[g]->m_lLayers.size() * 14.0f;
+	}
 
 	float ScrollDifference = LayersHeight - LayersBox.h;
 
@@ -1798,6 +2276,20 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 		LayersBox.VSplitRight(3.0f, &LayersBox, 0);	// extra spacing
 		Scroll.HMargin(5.0f, &Scroll);
 		s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
+
+		if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&LayersBox))
+		{
+			int ScrollNum = (int)((LayersHeight-LayersBox.h)/15.0f)+1;
+			if(ScrollNum > 0)
+			{
+				if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
+					s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f);
+				if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
+					s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f);
+			}
+			else
+				ScrollNum = 0;
+		}
 	}
 
 	float LayerStartAt = ScrollDifference * s_ScrollValue;
@@ -1824,7 +2316,7 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 			{
 				LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
 				Slot.VSplitLeft(12, &VisibleToggle, &Slot);
-				if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L))
+				if(DoButton_Ex(&m_Map.m_lGroups[g]->m_Visible, m_Map.m_lGroups[g]->m_Visible?"V":"H", m_Map.m_lGroups[g]->m_Collapse ? 1 : 0, &VisibleToggle, 0, "Toggle group visibility", CUI::CORNER_L))
 					m_Map.m_lGroups[g]->m_Visible = !m_Map.m_lGroups[g]->m_Visible;
 
 				Slot.VSplitRight(12.0f, &Slot, &SaveCheck);
@@ -1832,16 +2324,22 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 					if(!m_Map.m_lGroups[g]->m_GameGroup)
 						m_Map.m_lGroups[g]->m_SaveToMap = !m_Map.m_lGroups[g]->m_SaveToMap;
 
-				str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_pName);
+				str_format(aBuf, sizeof(aBuf),"#%d %s", g, m_Map.m_lGroups[g]->m_aName);
+				float FontSize = 10.0f;
+				while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Slot.w)
+					FontSize--;
 				if(int Result = DoButton_Ex(&m_Map.m_lGroups[g], aBuf, g==m_SelectedGroup, &Slot,
-					BUTTON_CONTEXT, "Select group. Right click for properties.", 0))
+					BUTTON_CONTEXT, m_Map.m_lGroups[g]->m_Collapse ? "Select group. Double click to expand." : "Select group. Double click to collapse.", 0, FontSize))
 				{
 					m_SelectedGroup = g;
 					m_SelectedLayer = 0;
 
 					static int s_GroupPopupId = 0;
 					if(Result == 2)
-						UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 120, 200, PopupGroup);
+						UiInvokePopupMenu(&s_GroupPopupId, 0, UI()->MouseX(), UI()->MouseY(), 145, 220, PopupGroup);
+
+					if(m_Map.m_lGroups[g]->m_lLayers.size() && Input()->MouseDoubleClick())
+						m_Map.m_lGroups[g]->m_Collapse ^= 1;
 				}
 				LayersBox.HSplitTop(2.0f, &Slot, &LayersBox);
 			}
@@ -1857,6 +2355,9 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 					continue;
 				}
 
+				if(m_Map.m_lGroups[g]->m_Collapse)
+					continue;
+
 				//visible
 				LayersBox.HSplitTop(12.0f, &Slot, &LayersBox);
 				Slot.VSplitLeft(12.0f, 0, &Button);
@@ -1870,15 +2371,24 @@ void CEditor::RenderLayers(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 					if(m_Map.m_lGroups[g]->m_lLayers[i] != m_Map.m_pGameLayer)
 						m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap = !m_Map.m_lGroups[g]->m_lLayers[i]->m_SaveToMap;
 
-				str_format(aBuf, sizeof(aBuf),"#%d %s ", i, m_Map.m_lGroups[g]->m_lLayers[i]->m_pTypeName);
+				if(m_Map.m_lGroups[g]->m_lLayers[i]->m_aName[0])
+					str_format(aBuf, sizeof(aBuf), "%s", m_Map.m_lGroups[g]->m_lLayers[i]->m_aName);
+				else if(m_Map.m_lGroups[g]->m_lLayers[i]->m_Type == LAYERTYPE_TILES)
+					str_copy(aBuf, "Tiles", sizeof(aBuf));
+				else
+					str_copy(aBuf, "Quads", sizeof(aBuf));
+
+				float FontSize = 10.0f;
+				while(TextRender()->TextWidth(0, FontSize, aBuf, -1) > Button.w)
+					FontSize--;
 				if(int Result = DoButton_Ex(m_Map.m_lGroups[g]->m_lLayers[i], aBuf, g==m_SelectedGroup&&i==m_SelectedLayer, &Button,
-					BUTTON_CONTEXT, "Select layer. Right click for properties.", 0))
+					BUTTON_CONTEXT, "Select layer.", 0, FontSize))
 				{
 					m_SelectedLayer = i;
 					m_SelectedGroup = g;
 					static int s_LayerPopupID = 0;
 					if(Result == 2)
-						UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 220, PopupLayer);
+						UiInvokePopupMenu(&s_LayerPopupID, 0, UI()->MouseX(), UI()->MouseY(), 120, 245, PopupLayer);
 				}
 
 				LayerCur += 14.0f;
@@ -1916,12 +2426,13 @@ void CEditor::ReplaceImage(const char *pFileName, int StorageType, void *pUser)
 	*pImg = ImgInfo;
 	pImg->m_External = External;
 	pEditor->ExtractName(pFileName, pImg->m_aName, sizeof(pImg->m_aName));
+	pImg->m_AutoMapper.Load(pImg->m_aName);
 	pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
 	pEditor->SortImages();
 	for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i)
 	{
-	    if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, pImg->m_aName))
-           pEditor->m_SelectedImage = i;
+		if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, pImg->m_aName))
+			pEditor->m_SelectedImage = i;
 	}
 	pEditor->m_Dialog = DIALOG_NONE;
 }
@@ -1938,8 +2449,8 @@ void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
 	ExtractName(pFileName, aBuf, sizeof(aBuf));
 	for(int i = 0; i < pEditor->m_Map.m_lImages.size(); ++i)
 	{
-	    if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, aBuf))
-            return;
+		if(!str_comp(pEditor->m_Map.m_lImages[i]->m_aName, aBuf))
+			return;
 	}
 
 	CEditorImage *pImg = new CEditorImage(pEditor);
@@ -1947,6 +2458,7 @@ void CEditor::AddImage(const char *pFileName, int StorageType, void *pUser)
 	pImg->m_TexID = pEditor->Graphics()->LoadTextureRaw(ImgInfo.m_Width, ImgInfo.m_Height, ImgInfo.m_Format, ImgInfo.m_pData, CImageInfo::FORMAT_AUTO, 0);
 	pImg->m_External = 1;	// external by default
 	str_copy(pImg->m_aName, aBuf, sizeof(pImg->m_aName));
+	pImg->m_AutoMapper.Load(pImg->m_aName);
 	pEditor->m_Map.m_lImages.add(pImg);
 	pEditor->SortImages();
 	if(pEditor->m_SelectedImage > -1 && pEditor->m_SelectedImage < pEditor->m_Map.m_lImages.size())
@@ -2064,7 +2576,7 @@ void CEditor::SortImages()
 		gs_pSortedIndex = 0;
 	}
 }
-	
+
 
 void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 {
@@ -2080,6 +2592,20 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 		ToolBox.VSplitRight(3.0f, &ToolBox, 0);	// extra spacing
 		Scroll.HMargin(5.0f, &Scroll);
 		s_ScrollValue = UiDoScrollbarV(&s_ScrollBar, &Scroll, s_ScrollValue);
+
+		if(UI()->MouseInside(&Scroll) || UI()->MouseInside(&ToolBox))
+		{
+			int ScrollNum = (int)((ImagesHeight-ToolBox.h)/14.0f)+1;
+			if(ScrollNum > 0)
+			{
+				if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
+					s_ScrollValue = clamp(s_ScrollValue - 1.0f/ScrollNum, 0.0f, 1.0f);
+				if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
+					s_ScrollValue = clamp(s_ScrollValue + 1.0f/ScrollNum, 0.0f, 1.0f);
+			}
+			else
+				ScrollNum = 0;
+		}
 	}
 
 	float ImageStartAt = ScrollDifference * s_ScrollValue;
@@ -2148,15 +2674,28 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 					r.w = r.h;
 				else
 					r.h = r.w;
+				float Max = (float)(max(m_Map.m_lImages[i]->m_Width, m_Map.m_lImages[i]->m_Height));
+				r.w *= m_Map.m_lImages[i]->m_Width/Max;
+				r.h *= m_Map.m_lImages[i]->m_Height/Max;
 				Graphics()->TextureSet(m_Map.m_lImages[i]->m_TexID);
 				Graphics()->BlendNormal();
+				Graphics()->WrapClamp();
 				Graphics()->QuadsBegin();
 				IGraphics::CQuadItem QuadItem(r.x, r.y, r.w, r.h);
 				Graphics()->QuadsDrawTL(&QuadItem, 1);
 				Graphics()->QuadsEnd();
-
+				Graphics()->WrapNormal();
 			}
 		}
+
+		// separator
+		ToolBox.HSplitTop(5.0f, &Slot, &ToolBox);
+		ImageCur += 5.0f;
+		IGraphics::CLineItem LineItem(Slot.x, Slot.y+Slot.h/2, Slot.x+Slot.w, Slot.y+Slot.h/2);
+		Graphics()->TextureSet(-1);
+		Graphics()->LinesBegin();
+		Graphics()->LinesDraw(&LineItem, 1);
+		Graphics()->LinesEnd();
 	}
 
 	if(ImageCur + 27.0f > ImageStopAt)
@@ -2167,7 +2706,6 @@ void CEditor::RenderImages(CUIRect ToolBox, CUIRect ToolBar, CUIRect View)
 
 	// new image
 	static int s_NewImageButton = 0;
-	ToolBox.HSplitTop(10.0f, &Slot, &ToolBox);
 	ToolBox.HSplitTop(12.0f, &Slot, &ToolBox);
 	if(DoButton_Editor(&s_NewImageButton, "Add", 0, &Slot, 0, "Load a new image to use in the map"))
 		InvokeFileDialog(IStorage::TYPE_ALL, FILETYPE_IMG, "Add Image", "Add", "mapres", "", AddImage, this);
@@ -2217,7 +2755,7 @@ void CEditor::AddFileDialogEntry(int Index, CUIRect *pView)
 	Graphics()->QuadsDrawTL(&QuadItem, 1);
 	Graphics()->QuadsEnd();
 
-	if(DoButton_File((void*)(10+(int)Button.y), m_FileList[Index].m_aName, m_FilesSelectedIndex == Index, &Button, 0, 0))
+	if(DoButton_File(&m_FileList[Index], m_FileList[Index].m_aName, m_FilesSelectedIndex == Index, &Button, 0, 0))
 	{
 		if(!m_FileList[Index].m_IsDir)
 			str_copy(m_aFileDialogFileName, m_FileList[Index].m_aFilename, sizeof(m_aFileDialogFileName));
@@ -2243,11 +2781,13 @@ void CEditor::RenderFileDialog()
 	RenderTools()->DrawUIRect(&View, vec4(0,0,0,0.75f), CUI::CORNER_ALL, 5.0f);
 	View.Margin(10.0f, &View);
 
-	CUIRect Title, FileBox, FileBoxLabel, ButtonBar, Scroll;
+	CUIRect Title, FileBox, FileBoxLabel, ButtonBar, Scroll, PathBox;
 	View.HSplitTop(18.0f, &Title, &View);
 	View.HSplitTop(5.0f, 0, &View); // some spacing
 	View.HSplitBottom(14.0f, &View, &ButtonBar);
 	View.HSplitBottom(10.0f, &View, 0); // some spacing
+	View.HSplitBottom(14.0f, &View, &PathBox);
+	View.HSplitBottom(5.0f, &View, 0); // some spacing
 	View.HSplitBottom(14.0f, &View, &FileBox);
 	FileBox.VSplitLeft(55.0f, &FileBoxLabel, &FileBox);
 	View.HSplitBottom(10.0f, &View, 0); // some spacing
@@ -2258,12 +2798,21 @@ void CEditor::RenderFileDialog()
 	Title.VMargin(10.0f, &Title);
 	UI()->DoLabel(&Title, m_pFileDialogTitle, 12.0f, -1, -1);
 
+	// pathbox
+	char aPath[128], aBuf[128];
+	if(m_FilesSelectedIndex != -1)
+		Storage()->GetCompletePath(m_FileList[m_FilesSelectedIndex].m_StorageType, m_pFileDialogPath, aPath, sizeof(aPath));
+	else
+		aPath[0] = 0;
+	str_format(aBuf, sizeof(aBuf), "Current path: %s", aPath);
+	UI()->DoLabel(&PathBox, aBuf, 10.0f, -1, -1);
+
 	// filebox
 	if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
 	{
-		static int s_FileBoxID = 0;
+		static float s_FileBoxID = 0;
 		UI()->DoLabel(&FileBoxLabel, "Filename:", 10.0f, -1, -1);
-		if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f))
+		if(DoEditBox(&s_FileBoxID, &FileBox, m_aFileDialogFileName, sizeof(m_aFileDialogFileName), 10.0f, &s_FileBoxID))
 		{
 			// remove '/' and '\'
 			for(int i = 0; m_aFileDialogFileName[i]; ++i)
@@ -2354,6 +2903,7 @@ void CEditor::RenderFileDialog()
 	static int s_OkButton = 0;
 	static int s_CancelButton = 0;
 	static int s_NewFolderButton = 0;
+	static int s_MapInfoButton = 0;
 
 	CUIRect Button;
 	ButtonBar.VSplitRight(50.0f, &ButtonBar, &Button);
@@ -2429,6 +2979,22 @@ void CEditor::RenderFileDialog()
 			UI()->SetActiveItem(0);
 		}
 	}
+
+	if(m_FileDialogStorageType == IStorage::TYPE_SAVE)
+	{
+		ButtonBar.VSplitLeft(40.0f, 0, &ButtonBar);
+		ButtonBar.VSplitLeft(70.0f, &Button, &ButtonBar);
+		if(DoButton_Editor(&s_MapInfoButton, "Map details", 0, &Button, 0, 0))
+		{
+			str_copy(m_Map.m_MapInfo.m_aAuthorTmp, m_Map.m_MapInfo.m_aAuthor, sizeof(m_Map.m_MapInfo.m_aAuthorTmp));
+			str_copy(m_Map.m_MapInfo.m_aVersionTmp, m_Map.m_MapInfo.m_aVersion, sizeof(m_Map.m_MapInfo.m_aVersionTmp));
+			str_copy(m_Map.m_MapInfo.m_aCreditsTmp, m_Map.m_MapInfo.m_aCredits, sizeof(m_Map.m_MapInfo.m_aCreditsTmp));
+			str_copy(m_Map.m_MapInfo.m_aLicenseTmp, m_Map.m_MapInfo.m_aLicense, sizeof(m_Map.m_MapInfo.m_aLicenseTmp));
+			static int s_MapInfoPopupID = 0;
+			UiInvokePopupMenu(&s_MapInfoPopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupMapInfo);
+			UI()->SetActiveItem(0);
+		}
+	}
 }
 
 void CEditor::FilelistPopulate(int StorageType)
@@ -2469,7 +3035,7 @@ void CEditor::InvokeFileDialog(int StorageType, int FileType, const char *pTitle
 		str_copy(m_aFileDialogFileName, pDefaultName, sizeof(m_aFileDialogFileName));
 	if(pBasePath)
 		str_copy(m_aFileDialogCurrentFolder, pBasePath, sizeof(m_aFileDialogCurrentFolder));
-	
+
 	FilelistPopulate(m_FileDialogStorageType);
 
 	m_Dialog = DIALOG_FILE;
@@ -2489,10 +3055,10 @@ void CEditor::RenderModebar(CUIRect View)
 		const char *pButName = m_Mode == MODE_LAYERS ? "Layers" : "Images";
 		if(DoButton_Tab(&s_Button, pButName, 0, &Button, 0, "Switch between images and layers managment."))
 		{
-		    if(m_Mode == MODE_LAYERS)
-                m_Mode = MODE_IMAGES;
-            else
-                m_Mode = MODE_LAYERS;
+			if(m_Mode == MODE_LAYERS)
+				m_Mode = MODE_IMAGES;
+			else
+				m_Mode = MODE_LAYERS;
 		}
 	}
 
@@ -2612,8 +3178,8 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 
 			ToolBar.VSplitLeft(80.0f, &Button, &ToolBar);
 
-			static int s_NameBox = 0;
-			if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f))
+			static float s_NameBox = 0;
+			if(DoEditBox(&s_NameBox, &Button, pEnvelope->m_aName, sizeof(pEnvelope->m_aName), 10.0f, &s_NameBox))
 				m_Map.m_Modified = true;
 		}
 	}
@@ -2666,6 +3232,17 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 				if(DoButton_Editor(&s_aChannelButtons[i], s_paNames[pEnvelope->m_Channels-3][i], s_ActiveChannels&Bit, &Button, 0, paDescriptions[pEnvelope->m_Channels-3][i]))
 					s_ActiveChannels ^= Bit;
 			}
+
+			// sync checkbox
+			ToolBar.VSplitLeft(15.0f, &Button, &ToolBar);
+			ToolBar.VSplitLeft(12.0f, &Button, &ToolBar);
+			static int s_SyncButton;
+			if(DoButton_Editor(&s_SyncButton, pEnvelope->m_Synchronized?"X":"", 0, &Button, 0, "Enable envelope synchronization between clients"))
+				pEnvelope->m_Synchronized = !pEnvelope->m_Synchronized;
+
+			ToolBar.VSplitLeft(4.0f, &Button, &ToolBar);
+			ToolBar.VSplitLeft(80.0f, &Button, &ToolBar);
+			UI()->DoLabel(&Button, "Synchronized", 10.0f, -1, -1);
 		}
 
 		float EndTime = pEnvelope->EndTime();
@@ -2705,6 +3282,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 					m_Map.m_Modified = true;
 				}
 
+				m_ShowEnvelopePreview = 1;
 				m_pTooltip = "Press right mouse button to create a new point";
 			}
 		}
@@ -2811,8 +3389,6 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 
 		// render handles
 		{
-			static bool s_Move = false;
-
 			int CurrentValue = 0, CurrentTime = 0;
 
 			Graphics()->TextureSet(-1);
@@ -2845,11 +3421,13 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 					{
 						if(!UI()->MouseButton(0))
 						{
+							m_SelectedQuadEnvelope = -1;
+							m_SelectedEnvelopePoint = -1;
+
 							UI()->SetActiveItem(0);
-							s_Move = false;
 						}
 						else
-						{							
+						{
 							if(Input()->KeyPressed(KEY_LSHIFT) || Input()->KeyPressed(KEY_RSHIFT))
 							{
 								if(i != 0)
@@ -2871,6 +3449,10 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 								else
 									pEnvelope->m_lPoints[i].m_aValues[c] -= f2fx(m_MouseDeltaY*ValueScale);
 							}
+
+							m_SelectedQuadEnvelope = m_SelectedEnvelope;
+							m_ShowEnvelopePreview = 1;
+							m_SelectedEnvelopePoint = i;
 							m_Map.m_Modified = true;
 						}
 
@@ -2893,6 +3475,7 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 							m_Map.m_Modified = true;
 						}
 
+						m_ShowEnvelopePreview = 1;
 						ColorMod = 100.0f;
 						Graphics()->SetColor(1,0.75f,0.75f,1);
 						m_pTooltip = "Left mouse to drag. Hold ctrl to be more precise. Hold shift to alter time point aswell. Right click to delete.";
@@ -2904,7 +3487,10 @@ void CEditor::RenderEnvelopeEditor(CUIRect View)
 						CurrentValue = pEnvelope->m_lPoints[i].m_aValues[c];
 					}
 
-					Graphics()->SetColor(aColors[c].r*ColorMod, aColors[c].g*ColorMod, aColors[c].b*ColorMod, 1.0f);
+					if (m_SelectedQuadEnvelope == m_SelectedEnvelope && m_SelectedEnvelopePoint == i)
+						Graphics()->SetColor(1.0f, 1.0f, 1.0f, 1.0f);
+					else
+						Graphics()->SetColor(aColors[c].r*ColorMod, aColors[c].g*ColorMod, aColors[c].b*ColorMod, 1.0f);
 					IGraphics::CQuadItem QuadItem(Final.x, Final.y, Final.w, Final.h);
 					Graphics()->QuadsDrawTL(&QuadItem, 1);
 				}
@@ -3027,10 +3613,15 @@ void CEditor::RenderMenubar(CUIRect MenuBar)
 		(void)0;
 		*/
 
+	CUIRect Info;
 	MenuBar.VSplitLeft(40.0f, 0, &MenuBar);
+	MenuBar.VSplitLeft(MenuBar.w*0.75f, &MenuBar, &Info);
 	char aBuf[128];
 	str_format(aBuf, sizeof(aBuf), "File: %s", m_aFileName);
 	UI()->DoLabel(&MenuBar, aBuf, 10.0f, -1, -1);
+
+	str_format(aBuf, sizeof(aBuf), "Z: %i, A: %.1f, G: %i", m_ZoomLevel, m_AnimateSpeed, m_GridFactor);
+	UI()->DoLabel(&Info, aBuf, 10.0f, 1, -1);
 }
 
 void CEditor::Render()
@@ -3046,10 +3637,14 @@ void CEditor::Render()
 	// reset tip
 	m_pTooltip = 0;
 
+	if(m_EditBoxActive)
+		--m_EditBoxActive;
+
 	// render checker
 	RenderBackground(View, ms_CheckerTexture, 32.0f, 1.0f);
 
 	CUIRect MenuBar, CModeBar, ToolBar, StatusBar, EnvelopeEditor, ToolBox;
+	m_ShowPicker = Input()->KeyPressed(KEY_SPACE) != 0 && m_Dialog == DIALOG_NONE;
 
 	if(m_GuiActive)
 	{
@@ -3059,7 +3654,7 @@ void CEditor::Render()
 		View.VSplitLeft(100.0f, &ToolBox, &View);
 		View.HSplitBottom(16.0f, &View, &StatusBar);
 
-		if(m_ShowEnvelopeEditor)
+		if(m_ShowEnvelopeEditor && !m_ShowPicker)
 		{
 			float size = 125.0f;
 			if(m_ShowEnvelopeEditor == 2)
@@ -3074,6 +3669,28 @@ void CEditor::Render()
 	if(m_Mode == MODE_LAYERS)
 		DoMapEditor(View, ToolBar);
 
+	// do zooming
+	if(Input()->KeyDown(KEY_KP_MINUS))
+		m_ZoomLevel += 50;
+	if(Input()->KeyDown(KEY_KP_PLUS))
+		m_ZoomLevel -= 50;
+	if(Input()->KeyDown(KEY_KP_MULTIPLY))
+	{
+		m_EditorOffsetX = 0;
+		m_EditorOffsetY = 0;
+		m_ZoomLevel = 100;
+	}
+	if(m_Dialog == DIALOG_NONE && UI()->MouseInside(&View))
+	{
+		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_UP))
+			m_ZoomLevel -= 20;
+
+		if(Input()->KeyPresses(KEY_MOUSE_WHEEL_DOWN))
+			m_ZoomLevel += 20;
+	}
+	m_ZoomLevel = clamp(m_ZoomLevel, 50, 2000);
+	m_WorldZoom = m_ZoomLevel/100.0f;
+
 	if(m_GuiActive)
 	{
 		float Brightness = 0.25f;
@@ -3130,6 +3747,7 @@ void CEditor::Render()
 		static int s_PopupID = 0;
 		UiInvokePopupMenu(&s_PopupID, 0, Width/2.0f-200.0f, Height/2.0f-100.0f, 400.0f, 200.0f, PopupEvent);
 		m_PopupEventActivated = false;
+		m_PopupEventWasActivated = true;
 	}
 
 
@@ -3171,7 +3789,6 @@ void CEditor::Render()
 		Graphics()->QuadsDrawTL(&QuadItem, 1);
 		Graphics()->QuadsEnd();
 	}
-
 }
 
 void CEditor::Reset(bool CreateDefault)
@@ -3192,12 +3809,12 @@ void CEditor::Reset(bool CreateDefault)
 	m_SelectedPoints = 0;
 	m_SelectedEnvelope = 0;
 	m_SelectedImage = 0;
-	
+
 	m_WorldOffsetX = 0;
 	m_WorldOffsetY = 0;
 	m_EditorOffsetX = 0.0f;
 	m_EditorOffsetY = 0.0f;
-	
+
 	m_WorldZoom = 1.0f;
 	m_ZoomLevel = 200;
 
@@ -3207,6 +3824,26 @@ void CEditor::Reset(bool CreateDefault)
 	m_MouseDeltaWy = 0;
 
 	m_Map.m_Modified = false;
+
+	m_ShowEnvelopePreview = 0;
+}
+
+int CEditor::GetLineDistance()
+{
+	int LineDistance = 512;
+
+	if(m_ZoomLevel <= 100)
+		LineDistance = 16;
+	else if(m_ZoomLevel <= 250)
+		LineDistance = 32;
+	else if(m_ZoomLevel <= 450)
+		LineDistance = 64;
+	else if(m_ZoomLevel <= 850)
+		LineDistance = 128;
+	else if(m_ZoomLevel <= 1550)
+		LineDistance = 256;
+
+	return LineDistance;
 }
 
 void CEditorMap::DeleteEnvelope(int Index)
@@ -3221,19 +3858,27 @@ void CEditorMap::DeleteEnvelope(int Index)
 		for(int j = 0; j < m_lGroups[i]->m_lLayers.size(); ++j)
 			if(m_lGroups[i]->m_lLayers[j]->m_Type == LAYERTYPE_QUADS)
 			{
-				CLayerQuads *Layer = static_cast<CLayerQuads *>(m_lGroups[i]->m_lLayers[j]);
-				for(int k = 0; k < Layer->m_lQuads.size(); ++k)
+				CLayerQuads *pLayer = static_cast<CLayerQuads *>(m_lGroups[i]->m_lLayers[j]);
+				for(int k = 0; k < pLayer->m_lQuads.size(); ++k)
 				{
-					if(Layer->m_lQuads[k].m_PosEnv == Index)
-						Layer->m_lQuads[k].m_PosEnv = -1;
-					else if(Layer->m_lQuads[k].m_PosEnv > Index)
-						Layer->m_lQuads[k].m_PosEnv--;
-					if(Layer->m_lQuads[k].m_ColorEnv == Index)
-						Layer->m_lQuads[k].m_ColorEnv = -1;
-					else if(Layer->m_lQuads[k].m_ColorEnv > Index)
-						Layer->m_lQuads[k].m_ColorEnv--;
+					if(pLayer->m_lQuads[k].m_PosEnv == Index)
+						pLayer->m_lQuads[k].m_PosEnv = -1;
+					else if(pLayer->m_lQuads[k].m_PosEnv > Index)
+						pLayer->m_lQuads[k].m_PosEnv--;
+					if(pLayer->m_lQuads[k].m_ColorEnv == Index)
+						pLayer->m_lQuads[k].m_ColorEnv = -1;
+					else if(pLayer->m_lQuads[k].m_ColorEnv > Index)
+						pLayer->m_lQuads[k].m_ColorEnv--;
 				}
 			}
+			else if(m_lGroups[i]->m_lLayers[j]->m_Type == LAYERTYPE_TILES)
+			{
+				CLayerTiles *pLayer = static_cast<CLayerTiles *>(m_lGroups[i]->m_lLayers[j]);
+				if(pLayer->m_ColorEnv == Index)
+					pLayer->m_ColorEnv = -1;
+				if(pLayer->m_ColorEnv > Index)
+					pLayer->m_ColorEnv--;
+			}
 
 	m_lEnvelopes.remove_index(Index);
 }
@@ -3249,7 +3894,7 @@ void CEditorMap::MakeGameGroup(CLayerGroup *pGroup)
 {
 	m_pGameGroup = pGroup;
 	m_pGameGroup->m_GameGroup = true;
-	m_pGameGroup->m_pName = "Game";
+	str_copy(m_pGameGroup->m_aName, "Game", sizeof(m_pGameGroup->m_aName));
 }
 
 
@@ -3260,6 +3905,8 @@ void CEditorMap::Clean()
 	m_lEnvelopes.delete_all();
 	m_lImages.delete_all();
 
+	m_MapInfo.Reset();
+
 	m_pGameLayer = 0x0;
 	m_pGameGroup = 0x0;
 
@@ -3325,19 +3972,19 @@ void CEditor::Init()
 
 void CEditor::DoMapBorder()
 {
-    CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
-    
-    for(int i = 0; i < pT->m_Width*2; ++i)
-        pT->m_pTiles[i].m_Index = 1;
-        
-    for(int i = 0; i < pT->m_Width*pT->m_Height; ++i)
-    {
-        if(i%pT->m_Width < 2 || i%pT->m_Width > pT->m_Width-3)
-            pT->m_pTiles[i].m_Index = 1;
-    }
-    
-    for(int i = (pT->m_Width*(pT->m_Height-2)); i < pT->m_Width*pT->m_Height; ++i)
-        pT->m_pTiles[i].m_Index = 1;
+	CLayerTiles *pT = (CLayerTiles *)GetSelectedLayerType(0, LAYERTYPE_TILES);
+
+	for(int i = 0; i < pT->m_Width*2; ++i)
+		pT->m_pTiles[i].m_Index = 1;
+
+	for(int i = 0; i < pT->m_Width*pT->m_Height; ++i)
+	{
+		if(i%pT->m_Width < 2 || i%pT->m_Width > pT->m_Width-3)
+			pT->m_pTiles[i].m_Index = 1;
+	}
+
+	for(int i = (pT->m_Width*(pT->m_Height-2)); i < pT->m_Width*pT->m_Height; ++i)
+		pT->m_pTiles[i].m_Index = 1;
 }
 
 void CEditor::UpdateAndRender()
@@ -3356,6 +4003,7 @@ void CEditor::UpdateAndRender()
 	float rx, ry;
 	{
 		Input()->MouseRelative(&rx, &ry);
+		UI()->ConvertMouseMove(&rx, &ry);
 		m_MouseDeltaX = rx;
 		m_MouseDeltaY = ry;
 
@@ -3402,18 +4050,6 @@ void CEditor::UpdateAndRender()
 	if(Input()->KeyDown(KEY_TAB))
 		m_GuiActive = !m_GuiActive;
 
-	if(Input()->KeyDown(KEY_F5))
-		Save("maps/debug_test2.map");
-
-	if(Input()->KeyDown(KEY_F6))
-		Load("maps/debug_test2.map", IStorage::TYPE_ALL);
-	
-	if(Input()->KeyDown(KEY_F8))
-		Load("maps/debug_test.map", IStorage::TYPE_ALL);
-	
-	if(Input()->KeyDown(KEY_F7))
-		Save("maps/quicksave.map");
-
 	if(Input()->KeyDown(KEY_F10))
 		m_ShowMousePointer = false;
 
diff --git a/src/game/editor/ed_editor.h b/src/game/editor/editor.h
index aab757e1..a81474d9 100644
--- a/src/game/editor/ed_editor.h
+++ b/src/game/editor/editor.h
@@ -1,25 +1,28 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
-#ifndef GAME_EDITOR_ED_EDITOR_H
-#define GAME_EDITOR_ED_EDITOR_H
+#ifndef GAME_EDITOR_EDITOR_H
+#define GAME_EDITOR_EDITOR_H
+
+#include <math.h>
 
-#include <base/system.h>
 #include <base/math.h>
-#include <base/tl/array.h>
+#include <base/system.h>
+
 #include <base/tl/algorithm.h>
+#include <base/tl/array.h>
 #include <base/tl/sorted_array.h>
 #include <base/tl/string.h>
 
-#include <math.h>
+#include <game/client/ui.h>
 #include <game/mapitems.h>
 #include <game/client/render.h>
 
-#include <engine/shared/datafile.h>
 #include <engine/shared/config.h>
+#include <engine/shared/datafile.h>
 #include <engine/editor.h>
 #include <engine/graphics.h>
 
-#include <game/client/ui.h>
+#include "auto_map.h"
 
 typedef void (*INDEX_MODIFY_FUNC)(int *pIndex);
 
@@ -48,6 +51,7 @@ public:
 	array<CEnvPoint> m_lPoints;
 	char m_aName[32];
 	float m_Bottom, m_Top;
+	bool m_Synchronized;
 
 	CEnvelope(int Chan)
 	{
@@ -55,6 +59,7 @@ public:
 		m_aName[0] = 0;
 		m_Bottom = 0;
 		m_Top = 0;
+		m_Synchronized = true;
 	}
 
 	void Resort()
@@ -123,7 +128,7 @@ public:
 	CLayer()
 	{
 		m_Type = LAYERTYPE_INVALID;
-		m_pTypeName = "(invalid)";
+		str_copy(m_aName, "(invalid)", sizeof(m_aName));
 		m_Visible = true;
 		m_Readonly = false;
 		m_SaveToMap = true;
@@ -153,7 +158,7 @@ public:
 
 	virtual void GetSize(float *w, float *h) { *w = 0; *h = 0;}
 
-	const char *m_pTypeName;
+	char m_aName[12];
 	int m_Type;
 	int m_Flags;
 
@@ -181,10 +186,11 @@ public:
 	int m_ClipW;
 	int m_ClipH;
 
-	const char *m_pName;
+	char m_aName[12];
 	bool m_GameGroup;
 	bool m_Visible;
 	bool m_SaveToMap;
+	bool m_Collapse;
 
 	CLayerGroup();
 	~CLayerGroup();
@@ -230,6 +236,7 @@ public:
 	CEditor *m_pEditor;
 
 	CEditorImage(CEditor *pEditor)
+	: m_AutoMapper(pEditor)
 	{
 		m_pEditor = pEditor;
 		m_TexID = -1;
@@ -249,6 +256,7 @@ public:
 	int m_External;
 	char m_aName[128];
 	unsigned char m_aTileFlags[256];
+	class CAutoMapper m_AutoMapper;
 };
 
 class CEditorMap
@@ -268,6 +276,34 @@ public:
 	array<CEditorImage*> m_lImages;
 	array<CEnvelope*> m_lEnvelopes;
 
+	class CMapInfo
+	{
+	public:
+		char m_aAuthorTmp[32];
+		char m_aVersionTmp[16];
+		char m_aCreditsTmp[128];
+		char m_aLicenseTmp[32];
+
+		char m_aAuthor[32];
+		char m_aVersion[16];
+		char m_aCredits[128];
+		char m_aLicense[32];
+
+		void Reset()
+		{
+			m_aAuthorTmp[0] = 0;
+			m_aVersionTmp[0] = 0;
+			m_aCreditsTmp[0] = 0;
+			m_aLicenseTmp[0] = 0;
+
+			m_aAuthor[0] = 0;
+			m_aVersion[0] = 0;
+			m_aCredits[0] = 0;
+			m_aLicense[0] = 0;
+		}
+	};
+	CMapInfo m_MapInfo;
+
 	class CLayerGame *m_pGameLayer;
 	CLayerGroup *m_pGameGroup;
 
@@ -400,6 +436,8 @@ public:
 	int m_Width;
 	int m_Height;
 	CColor m_Color;
+	int m_ColorEnv;
+	int m_ColorEnvOffset;
 	CTile *m_pTiles;
 };
 
@@ -468,13 +506,18 @@ public:
 
 		m_Mode = MODE_LAYERS;
 		m_Dialog = 0;
+		m_EditBoxActive = 0;
 		m_pTooltip = 0;
 
+		m_GridActive = false;
+		m_GridFactor = 1;
+
 		m_aFileName[0] = 0;
 		m_aFileSaveName[0] = 0;
 		m_ValidSaveFilename = false;
 
 		m_PopupEventActivated = false;
+		m_PopupEventWasActivated = false;
 
 		m_FileDialogStorageType = 0;
 		m_pFileDialogTitle = 0;
@@ -517,6 +560,10 @@ public:
 
 		m_ShowEnvelopeEditor = 0;
 
+		m_ShowEnvelopePreview = 0;
+		m_SelectedQuadEnvelope = -1;
+		m_SelectedEnvelopePoint = -1;
+
 		ms_CheckerTexture = 0;
 		ms_BackgroundTexture = 0;
 		ms_CursorTexture = 0;
@@ -549,8 +596,12 @@ public:
 
 	int m_Mode;
 	int m_Dialog;
+	int m_EditBoxActive;
 	const char *m_pTooltip;
 
+	bool m_GridActive;
+	int m_GridFactor;
+
 	char m_aFileName[512];
 	char m_aFileSaveName[512];
 	bool m_ValidSaveFilename;
@@ -565,6 +616,7 @@ public:
 
 	int m_PopupEventType;
 	int m_PopupEventActivated;
+	int m_PopupEventWasActivated;
 
 	enum
 	{
@@ -630,12 +682,16 @@ public:
 	float m_AnimateSpeed;
 
 	int m_ShowEnvelopeEditor;
+	int m_ShowEnvelopePreview; //Values: 0-Off|1-Selected Envelope|2-All
+	bool m_ShowPicker;
 
 	int m_SelectedLayer;
 	int m_SelectedGroup;
 	int m_SelectedQuad;
 	int m_SelectedPoints;
 	int m_SelectedEnvelope;
+	int m_SelectedEnvelopePoint;
+    int m_SelectedQuadEnvelope;
 	int m_SelectedImage;
 
 	static int ms_CheckerTexture;
@@ -650,12 +706,14 @@ public:
 
 	CEditorMap m_Map;
 
+	static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser);
+
 	void DoMapBorder();
 	int DoButton_Editor_Common(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 	int DoButton_Editor(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 
 	int DoButton_Tab(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
-	int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners);
+	int DoButton_Ex(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip, int Corners, float FontSize=10.0f);
 	int DoButton_ButtonDec(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 	int DoButton_ButtonInc(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 
@@ -664,10 +722,12 @@ public:
 	int DoButton_Menu(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags, const char *pToolTip);
 	int DoButton_MenuItem(const void *pID, const char *pText, int Checked, const CUIRect *pRect, int Flags=0, const char *pToolTip=0);
 
-	int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, bool Hidden=false);
+	int DoEditBox(void *pID, const CUIRect *pRect, char *pStr, unsigned StrSize, float FontSize, float *Offset, bool Hidden=false, int Corners=CUI::CORNER_ALL);
 
 	void RenderBackground(CUIRect View, int Texture, float Size, float Brightness);
 
+	void RenderGrid(CLayerGroup *pGroup);
+
 	void UiInvokePopupMenu(void *pID, int Flags, float X, float Y, float W, float H, int (*pfnFunc)(CEditor *pEditor, CUIRect Rect), void *pExtra=0);
 	void UiDoPopupMenu();
 
@@ -678,11 +738,13 @@ public:
 	static int PopupQuad(CEditor *pEditor, CUIRect View);
 	static int PopupPoint(CEditor *pEditor, CUIRect View);
 	static int PopupNewFolder(CEditor *pEditor, CUIRect View);
+	static int PopupMapInfo(CEditor *pEditor, CUIRect View);
 	static int PopupEvent(CEditor *pEditor, CUIRect View);
 	static int PopupSelectImage(CEditor *pEditor, CUIRect View);
 	static int PopupSelectGametileOp(CEditor *pEditor, CUIRect View);
 	static int PopupImage(CEditor *pEditor, CUIRect View);
 	static int PopupMenuFile(CEditor *pEditor, CUIRect View);
+	static int PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View);
 
 	static void CallbackOpenMap(const char *pFileName, int StorageType, void *pUser);
 	static void CallbackAppendMap(const char *pFileName, int StorageType, void *pUser);
@@ -694,9 +756,15 @@ public:
 	void PopupSelectGametileOpInvoke(float x, float y);
 	int PopupSelectGameTileOpResult();
 
+	void PopupSelectConfigAutoMapInvoke(float x, float y);
+	int PopupSelectConfigAutoMapResult();
+
 	vec4 ButtonColorMul(const void *pID);
 
+	void DoQuadEnvelopes(CQuad *pQuad, int Index, int TexID = -1);
+	void DoQuadEnvPoint(CQuad *pQuad, int QIndex, int pIndex);
 	void DoQuadPoint(CQuad *pQuad, int QuadIndex, int v);
+
 	void DoMapEditor(CUIRect View, CUIRect Toolbar);
 	void DoToolbar(CUIRect Toolbar);
 	void DoQuad(CQuad *pQuad, int Index);
@@ -732,6 +800,8 @@ public:
 		int Length = pEnd > pExtractedName ? min(BufferSize, (int)(pEnd-pExtractedName+1)) : BufferSize;
 		str_copy(pName, pExtractedName, Length);
 	}
+
+	int GetLineDistance();
 };
 
 // make sure to inline this function
diff --git a/src/game/editor/ed_io.cpp b/src/game/editor/io.cpp
index a5ead97a..463147e1 100644
--- a/src/game/editor/ed_io.cpp
+++ b/src/game/editor/io.cpp
@@ -6,7 +6,7 @@
 #include <engine/serverbrowser.h>
 #include <engine/storage.h>
 #include <game/gamecore.h>
-#include "ed_editor.h"
+#include "editor.h"
 
 template<typename T>
 static int MakeVersion(int i, const T &v)
@@ -220,6 +220,31 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 		df.AddItem(MAPITEMTYPE_VERSION, 0, sizeof(Item), &Item);
 	}
 
+	// save map info
+	{
+		CMapItemInfo Item;
+		Item.m_Version = 1;
+
+		if(m_MapInfo.m_aAuthor[0])
+			Item.m_Author = df.AddData(str_length(m_MapInfo.m_aAuthor)+1, m_MapInfo.m_aAuthor);
+		else
+			Item.m_Author = -1;
+		if(m_MapInfo.m_aVersion[0])
+			Item.m_MapVersion = df.AddData(str_length(m_MapInfo.m_aVersion)+1, m_MapInfo.m_aVersion);
+		else
+			Item.m_MapVersion = -1;
+		if(m_MapInfo.m_aCredits[0])
+			Item.m_Credits = df.AddData(str_length(m_MapInfo.m_aCredits)+1, m_MapInfo.m_aCredits);
+		else
+			Item.m_Credits = -1;
+		if(m_MapInfo.m_aLicense[0])
+			Item.m_License = df.AddData(str_length(m_MapInfo.m_aLicense)+1, m_MapInfo.m_aLicense);
+		else
+			Item.m_License = -1;
+
+		df.AddItem(MAPITEMTYPE_INFO, 0, sizeof(Item), &Item);
+	}
+
 	// save images
 	for(int i = 0; i < m_lImages.size(); i++)
 	{
@@ -266,6 +291,9 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 		GItem.m_StartLayer = LayerCount;
 		GItem.m_NumLayers = 0;
 
+		// save group name
+		StrToInts(GItem.m_aName, sizeof(GItem.m_aName)/sizeof(int), pGroup->m_aName);
+
 		for(int l = 0; l < pGroup->m_lLayers.size(); l++)
 		{
 			if(!pGroup->m_lLayers[l]->m_SaveToMap)
@@ -278,23 +306,24 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 				pLayer->PrepareForSave();
 
 				CMapItemLayerTilemap Item;
-				Item.m_Version = 2;
+				Item.m_Version = 3;
 
 				Item.m_Layer.m_Flags = pLayer->m_Flags;
 				Item.m_Layer.m_Type = pLayer->m_Type;
 
-				Item.m_Color.r = pLayer->m_Color.r;
-				Item.m_Color.g = pLayer->m_Color.g;
-				Item.m_Color.b = pLayer->m_Color.b;
-				Item.m_Color.a = pLayer->m_Color.a;
-				Item.m_ColorEnv = -1; // not in use right now
-				Item.m_ColorEnvOffset = 0;
+				Item.m_Color = pLayer->m_Color;
+				Item.m_ColorEnv = pLayer->m_ColorEnv;
+				Item.m_ColorEnvOffset = pLayer->m_ColorEnvOffset;
 
 				Item.m_Width = pLayer->m_Width;
 				Item.m_Height = pLayer->m_Height;
-				Item.m_Flags = pLayer->m_Game;
+				Item.m_Flags = pLayer->m_Game ? TILESLAYERFLAG_GAME : 0;
 				Item.m_Image = pLayer->m_Image;
 				Item.m_Data = df.AddData(pLayer->m_Width*pLayer->m_Height*sizeof(CTile), pLayer->m_pTiles);
+
+				// save layer name
+				StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName);
+
 				df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item);
 
 				GItem.m_NumLayers++;
@@ -307,7 +336,7 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 				if(pLayer->m_lQuads.size())
 				{
 					CMapItemLayerQuads Item;
-					Item.m_Version = 1;
+					Item.m_Version = 2;
 					Item.m_Layer.m_Flags = pLayer->m_Flags;
 					Item.m_Layer.m_Type = pLayer->m_Type;
 					Item.m_Image = pLayer->m_Image;
@@ -315,6 +344,10 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 					// add the data
 					Item.m_NumQuads = pLayer->m_lQuads.size();
 					Item.m_Data = df.AddDataSwapped(pLayer->m_lQuads.size()*sizeof(CQuad), pLayer->m_lQuads.base_ptr());
+
+					// save layer name
+					StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), pLayer->m_aName);
+
 					df.AddItem(MAPITEMTYPE_LAYER, LayerCount, sizeof(Item), &Item);
 
 					// clean up
@@ -334,10 +367,11 @@ int CEditorMap::Save(class IStorage *pStorage, const char *pFileName)
 	for(int e = 0; e < m_lEnvelopes.size(); e++)
 	{
 		CMapItemEnvelope Item;
-		Item.m_Version = 1;
+		Item.m_Version = CMapItemEnvelope::CURRENT_VERSION;
 		Item.m_Channels = m_lEnvelopes[e]->m_Channels;
 		Item.m_StartPoint = PointCount;
 		Item.m_NumPoints = m_lEnvelopes[e]->m_lPoints.size();
+		Item.m_Synchronized = m_lEnvelopes[e]->m_Synchronized;
 		StrToInts(Item.m_aName, sizeof(Item.m_aName)/sizeof(int), m_lEnvelopes[e]->m_aName);
 
 		df.AddItem(MAPITEMTYPE_ENVELOPE, e, sizeof(Item), &Item);
@@ -405,6 +439,22 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 	{
 		//editor.reset(false);
 
+		// load map info
+		{
+			CMapItemInfo *pItem = (CMapItemInfo *)DataFile.FindItem(MAPITEMTYPE_INFO, 0);
+			if(pItem && pItem->m_Version == 1)
+			{
+				if(pItem->m_Author > -1)
+					str_copy(m_MapInfo.m_aAuthor, (char *)DataFile.GetData(pItem->m_Author), sizeof(m_MapInfo.m_aAuthor));
+				if(pItem->m_MapVersion > -1)
+					str_copy(m_MapInfo.m_aVersion, (char *)DataFile.GetData(pItem->m_MapVersion), sizeof(m_MapInfo.m_aVersion));
+				if(pItem->m_Credits > -1)
+					str_copy(m_MapInfo.m_aCredits, (char *)DataFile.GetData(pItem->m_Credits), sizeof(m_MapInfo.m_aCredits));
+				if(pItem->m_License > -1)
+					str_copy(m_MapInfo.m_aLicense, (char *)DataFile.GetData(pItem->m_License), sizeof(m_MapInfo.m_aLicense));
+			}
+		}
+
 		// load images
 		{
 			int Start, Num;
@@ -449,6 +499,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 				if(pName)
 					str_copy(pImg->m_aName, pName, 128);
 
+				// load auto mapper file
+				pImg->m_AutoMapper.Load(pImg->m_aName);
+
 				m_lImages.add(pImg);
 
 				// unload image
@@ -486,6 +539,10 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 					pGroup->m_ClipH = pGItem->m_ClipH;
 				}
 
+				// load group name
+				if(pGItem->m_Version >= 3)
+					IntsToStr(pGItem->m_aName, sizeof(pGroup->m_aName)/sizeof(int), pGroup->m_aName);
+
 				for(int l = 0; l < pGItem->m_NumLayers; l++)
 				{
 					CLayer *pLayer = 0;
@@ -498,7 +555,7 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 						CMapItemLayerTilemap *pTilemapItem = (CMapItemLayerTilemap *)pLayerItem;
 						CLayerTiles *pTiles = 0;
 
-						if(pTilemapItem->m_Flags&1)
+						if(pTilemapItem->m_Flags&TILESLAYERFLAG_GAME)
 						{
 							pTiles = new CLayerGame(pTilemapItem->m_Width, pTilemapItem->m_Height);
 							MakeGameLayer(pTiles);
@@ -508,10 +565,9 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 						{
 							pTiles = new CLayerTiles(pTilemapItem->m_Width, pTilemapItem->m_Height);
 							pTiles->m_pEditor = m_pEditor;
-							pTiles->m_Color.r = pTilemapItem->m_Color.r;
-							pTiles->m_Color.g = pTilemapItem->m_Color.g;
-							pTiles->m_Color.b = pTilemapItem->m_Color.b;
-							pTiles->m_Color.a = pTilemapItem->m_Color.a;
+							pTiles->m_Color = pTilemapItem->m_Color;
+							pTiles->m_ColorEnv = pTilemapItem->m_ColorEnv;
+							pTiles->m_ColorEnvOffset = pTilemapItem->m_ColorEnvOffset;
 						}
 
 						pLayer = pTiles;
@@ -519,7 +575,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 						pGroup->AddLayer(pTiles);
 						void *pData = DataFile.GetData(pTilemapItem->m_Data);
 						pTiles->m_Image = pTilemapItem->m_Image;
-						pTiles->m_Game = pTilemapItem->m_Flags&1;
+						pTiles->m_Game = pTilemapItem->m_Flags&TILESLAYERFLAG_GAME;
+
+						// load layer name
+						if(pTilemapItem->m_Version >= 3)
+							IntsToStr(pTilemapItem->m_aName, sizeof(pTiles->m_aName)/sizeof(int), pTiles->m_aName);
 
 						mem_copy(pTiles->m_pTiles, pData, pTiles->m_Width*pTiles->m_Height*sizeof(CTile));
 
@@ -543,6 +603,11 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 						pQuads->m_Image = pQuadsItem->m_Image;
 						if(pQuads->m_Image < -1 || pQuads->m_Image >= m_lImages.size())
 							pQuads->m_Image = -1;
+
+						// load layer name
+						if(pQuadsItem->m_Version >= 2)
+							IntsToStr(pQuadsItem->m_aName, sizeof(pQuads->m_aName)/sizeof(int), pQuads->m_aName);
+
 						void *pData = DataFile.GetDataSwapped(pQuadsItem->m_Data);
 						pGroup->AddLayer(pQuads);
 						pQuads->m_lQuads.set_size(pQuadsItem->m_NumQuads);
@@ -578,6 +643,8 @@ int CEditorMap::Load(class IStorage *pStorage, const char *pFileName, int Storag
 				if(pItem->m_aName[0] != -1)	// compatibility with old maps
 					IntsToStr(pItem->m_aName, sizeof(pItem->m_aName)/sizeof(int), pEnv->m_aName);
 				m_lEnvelopes.add(pEnv);
+				if(pItem->m_Version >= 2)
+					pEnv->m_Synchronized = pItem->m_Synchronized;
 			}
 		}
 	}
diff --git a/src/game/editor/ed_layer_game.cpp b/src/game/editor/layer_game.cpp
index cf48845e..7e879c3e 100644
--- a/src/game/editor/ed_layer_game.cpp
+++ b/src/game/editor/layer_game.cpp
@@ -1,12 +1,12 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
-#include "ed_editor.h"
+#include "editor.h"
 
 
 CLayerGame::CLayerGame(int w, int h)
 : CLayerTiles(w, h)
 {
-	m_pTypeName = "Game";
+	str_copy(m_aName, "Game", sizeof(m_aName));
 	m_Game = 1;
 }
 
diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/layer_quads.cpp
index 680a54cd..d0b66405 100644
--- a/src/game/editor/ed_layer_quads.cpp
+++ b/src/game/editor/layer_quads.cpp
@@ -5,7 +5,7 @@
 #include <engine/console.h>
 #include <engine/graphics.h>
 
-#include "ed_editor.h"
+#include "editor.h"
 #include <game/generated/client_data.h>
 #include <game/client/render.h>
 #include <game/localization.h>
@@ -13,7 +13,7 @@
 CLayerQuads::CLayerQuads()
 {
 	m_Type = LAYERTYPE_QUADS;
-	m_pTypeName = "Quads";
+	str_copy(m_aName, "Quads", sizeof(m_aName));
 	m_Image = -1;
 }
 
@@ -21,31 +21,13 @@ CLayerQuads::~CLayerQuads()
 {
 }
 
-static void EnvelopeEval(float TimeOffset, int Env, float *pChannels, void *pUser)
-{
-	CEditor *pEditor = (CEditor *)pUser;
-	if(Env < 0 || Env > pEditor->m_Map.m_lEnvelopes.size())
-	{
-		pChannels[0] = 0;
-		pChannels[1] = 0;
-		pChannels[2] = 0;
-		pChannels[3] = 0;
-		return;
-	}
-
-	CEnvelope *e = pEditor->m_Map.m_lEnvelopes[Env];
-	float t = pEditor->m_AnimateTime+TimeOffset;
-	t *= pEditor->m_AnimateSpeed;
-	e->Eval(t, pChannels);
-}
-
 void CLayerQuads::Render()
 {
 	Graphics()->TextureSet(-1);
 	if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size())
 		Graphics()->TextureSet(m_pEditor->m_Map.m_lImages[m_Image]->m_TexID);
 
-	m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, EnvelopeEval, m_pEditor);
+	m_pEditor->RenderTools()->RenderQuads(m_lQuads.base_ptr(), m_lQuads.size(), LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT, m_pEditor->EnvelopeEval, m_pEditor);
 }
 
 CQuad *CLayerQuads::NewQuad()
diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/layer_tiles.cpp
index d0c9041c..9a21e5ce 100644
--- a/src/game/editor/ed_layer_tiles.cpp
+++ b/src/game/editor/layer_tiles.cpp
@@ -8,14 +8,14 @@
 
 #include <game/generated/client_data.h>
 #include <game/client/render.h>
-#include "ed_editor.h"
+#include "editor.h"
 
 #include <game/localization.h>
 
 CLayerTiles::CLayerTiles(int w, int h)
 {
 	m_Type = LAYERTYPE_TILES;
-	m_pTypeName = "Tiles";
+	str_copy(m_aName, "Tiles", sizeof(m_aName));
 	m_Width = w;
 	m_Height = h;
 	m_Image = -1;
@@ -25,6 +25,8 @@ CLayerTiles::CLayerTiles(int w, int h)
 	m_Color.g = 255;
 	m_Color.b = 255;
 	m_Color.a = 255;
+	m_ColorEnv = -1;
+	m_ColorEnvOffset = 0;
 
 	m_pTiles = new CTile[m_Width*m_Height];
 	mem_zero(m_pTiles, m_Width*m_Height*sizeof(CTile));
@@ -62,7 +64,8 @@ void CLayerTiles::Render()
 		m_TexID = m_pEditor->m_Map.m_lImages[m_Image]->m_TexID;
 	Graphics()->TextureSet(m_TexID);
 	vec4 Color = vec4(m_Color.r/255.0f, m_Color.g/255.0f, m_Color.b/255.0f, m_Color.a/255.0f);
-	m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT);
+	m_pEditor->RenderTools()->RenderTilemap(m_pTiles, m_Width, m_Height, 32.0f, Color, LAYERRENDERFLAG_OPAQUE|LAYERRENDERFLAG_TRANSPARENT,
+												m_pEditor->EnvelopeEval, m_pEditor, m_ColorEnv, m_ColorEnvOffset);
 }
 
 int CLayerTiles::ConvertX(float x) const { return (int)(x/32.0f); }
@@ -123,7 +126,7 @@ void CLayerTiles::BrushSelecting(CUIRect Rect)
 	m_pEditor->Graphics()->QuadsEnd();
 	char aBuf[16];
 	str_format(aBuf, sizeof(aBuf), "%d,%d", ConvertX(Rect.w), ConvertY(Rect.h));
-	TextRender()->Text(0, Rect.x+3.0f, Rect.y+3.0f, 15.0f*m_pEditor->m_WorldZoom, aBuf, -1);
+	TextRender()->Text(0, Rect.x+3.0f, Rect.y+3.0f, m_pEditor->m_ShowPicker?15.0f:15.0f*m_pEditor->m_WorldZoom, aBuf, -1);
 }
 
 int CLayerTiles::BrushGrab(CLayerGroup *pBrush, CUIRect Rect)
@@ -156,6 +159,8 @@ void CLayerTiles::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect)
 	if(m_Readonly)
 		return;
 
+	Snap(&Rect);
+
 	int sx = ConvertX(Rect.x);
 	int sy = ConvertY(Rect.y);
 	int w = ConvertX(Rect.w);
@@ -163,9 +168,9 @@ void CLayerTiles::FillSelection(bool Empty, CLayer *pBrush, CUIRect Rect)
 
 	CLayerTiles *pLt = static_cast<CLayerTiles*>(pBrush);
 
-	for(int y = 0; y <= h; y++)
+	for(int y = 0; y < h; y++)
 	{
-		for(int x = 0; x <= w; x++)
+		for(int x = 0; x < w; x++)
 		{
 			int fx = x+sx;
 			int fy = y+sy;
@@ -353,19 +358,39 @@ void CLayerTiles::ShowInfo()
 			}
 			x += m_pTiles[c].m_Skip;
 		}
+
+	Graphics()->MapScreen(ScreenX0, ScreenY0, ScreenX1, ScreenY1);
 }
 
 int CLayerTiles::RenderProperties(CUIRect *pToolBox)
 {
 	CUIRect Button;
-	pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
 
 	bool InGameGroup = !find_linear(m_pEditor->m_Map.m_pGameGroup->m_lLayers.all(), this).empty();
-	if(m_pEditor->m_Map.m_pGameLayer == this)
+	if(m_pEditor->m_Map.m_pGameLayer != this)
+	{
+		if(m_Image >= 0 && m_Image < m_pEditor->m_Map.m_lImages.size() && m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.IsLoaded())
+		{
+			static int s_AutoMapperButton = 0;
+			pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
+			if(m_pEditor->DoButton_Editor(&s_AutoMapperButton, "Auto map", 0, &Button, 0, ""))
+				m_pEditor->PopupSelectConfigAutoMapInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY());
+
+			int Result = m_pEditor->PopupSelectConfigAutoMapResult();
+			if(Result > -1)
+			{
+				m_pEditor->m_Map.m_lImages[m_Image]->m_AutoMapper.Proceed(this, Result);
+				return 1;
+			}
+		}
+	}
+	else
 		InGameGroup = false;
 
 	if(InGameGroup)
 	{
+		pToolBox->HSplitBottom(2.0f, pToolBox, 0);
+		pToolBox->HSplitBottom(12.0f, pToolBox, &Button);
 		static int s_ColclButton = 0;
 		if(m_pEditor->DoButton_Editor(&s_ColclButton, "Game tiles", 0, &Button, 0, "Constructs game tiles from this layer"))
 			m_pEditor->PopupSelectGametileOpInvoke(m_pEditor->UI()->MouseX(), m_pEditor->UI()->MouseY());
@@ -392,6 +417,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
 		PROP_SHIFT,
 		PROP_IMAGE,
 		PROP_COLOR,
+		PROP_COLOR_ENV,
+		PROP_COLOR_ENV_OFFSET,
 		NUM_PROPS,
 	};
 
@@ -407,6 +434,8 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
 		{"Shift", 0, PROPTYPE_SHIFT, 0, 0},
 		{"Image", m_Image, PROPTYPE_IMAGE, 0, 0},
 		{"Color", Color, PROPTYPE_COLOR, 0, 0},
+		{"Color Env", m_ColorEnv+1, PROPTYPE_INT_STEP, 0, m_pEditor->m_Map.m_lEnvelopes.size()+1},
+		{"Color TO", m_ColorEnvOffset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
 		{0},
 	};
 
@@ -445,6 +474,22 @@ int CLayerTiles::RenderProperties(CUIRect *pToolBox)
 		m_Color.b = (NewVal>>8)&0xff;
 		m_Color.a = NewVal&0xff;
 	}
+	if(Prop == PROP_COLOR_ENV)
+	{
+		int Index = clamp(NewVal-1, -1, m_pEditor->m_Map.m_lEnvelopes.size()-1);
+		int Step = (Index-m_ColorEnv)%2;
+		if(Step != 0)
+		{
+			for(; Index >= -1 && Index < m_pEditor->m_Map.m_lEnvelopes.size(); Index += Step)
+				if(Index == -1 || m_pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 4)
+				{
+					m_ColorEnv = Index;
+					break;
+				}
+		}
+	}
+	if(Prop == PROP_COLOR_ENV_OFFSET)
+		m_ColorEnvOffset = NewVal;
 
 	return 0;
 }
diff --git a/src/game/editor/ed_popups.cpp b/src/game/editor/popups.cpp
index f572e43c..2382823d 100644
--- a/src/game/editor/ed_popups.cpp
+++ b/src/game/editor/popups.cpp
@@ -1,11 +1,15 @@
 /* (c) Magnus Auvinen. See licence.txt in the root of the distribution for more information. */
 /* If you are missing that file, acquire a complete release at teeworlds.com.                */
+
+#include <base/tl/array.h>
+
 #include <engine/console.h>
 #include <engine/graphics.h>
 #include <engine/input.h>
 #include <engine/keys.h>
 #include <engine/storage.h>
-#include "ed_editor.h"
+
+#include "editor.h"
 
 
 // popup menu handling
@@ -147,6 +151,7 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View)
 		l->m_pEditor = pEditor;
 		pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l);
 		pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1;
+		pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
 		return 1;
 	}
 
@@ -160,9 +165,22 @@ int CEditor::PopupGroup(CEditor *pEditor, CUIRect View)
 		l->m_pEditor = pEditor;
 		pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->AddLayer(l);
 		pEditor->m_SelectedLayer = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_lLayers.size()-1;
+		pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_Collapse = false;
 		return 1;
 	}
 
+	// group name
+	if(!pEditor->GetSelectedGroup()->m_GameGroup)
+	{
+		View.HSplitBottom(5.0f, &View, &Button);
+		View.HSplitBottom(12.0f, &View, &Button);
+		static float s_Name = 0;
+		pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1);
+		Button.VSplitLeft(40.0f, 0, &Button);
+		if(pEditor->DoEditBox(&s_Name, &Button, pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_aName, sizeof(pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup]->m_aName), 10.0f, &s_Name))
+			pEditor->m_Map.m_Modified = true;
+	}
+
 	enum
 	{
 		PROP_ORDER=0,
@@ -239,6 +257,18 @@ int CEditor::PopupLayer(CEditor *pEditor, CUIRect View)
 		return 1;
 	}
 
+	// layer name
+	if(pEditor->m_Map.m_pGameLayer != pEditor->GetSelectedLayer(0))
+	{
+		View.HSplitBottom(5.0f, &View, &Button);
+		View.HSplitBottom(12.0f, &View, &Button);
+		static float s_Name = 0;
+		pEditor->UI()->DoLabel(&Button, "Name:", 10.0f, -1, -1);
+		Button.VSplitLeft(40.0f, 0, &Button);
+		if(pEditor->DoEditBox(&s_Name, &Button, pEditor->GetSelectedLayer(0)->m_aName, sizeof(pEditor->GetSelectedLayer(0)->m_aName), 10.0f, &s_Name))
+			pEditor->m_Map.m_Modified = true;
+	}
+
 	View.HSplitBottom(10.0f, &View, 0);
 
 	CLayerGroup *pCurrentGroup = pEditor->m_Map.m_lGroups[pEditor->m_SelectedGroup];
@@ -428,9 +458,35 @@ int CEditor::PopupQuad(CEditor *pEditor, CUIRect View)
 		for(int k = 0; k < 5; ++k)
 			pQuad->m_aPoints[k].y += Offset;
 	}
-	if(Prop == PROP_POS_ENV) pQuad->m_PosEnv = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1);
+	if(Prop == PROP_POS_ENV)
+	{
+		int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1);
+		int Step = (Index-pQuad->m_PosEnv)%2;
+		if(Step != 0)
+		{
+			for(; Index >= -1 && Index < pEditor->m_Map.m_lEnvelopes.size(); Index += Step)
+				if(Index == -1 || pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 3)
+				{
+					pQuad->m_PosEnv = Index;
+					break;
+				}
+		}
+	}
 	if(Prop == PROP_POS_ENV_OFFSET) pQuad->m_PosEnvOffset = NewVal;
-	if(Prop == PROP_COLOR_ENV) pQuad->m_ColorEnv = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1);
+	if(Prop == PROP_COLOR_ENV)
+	{
+		int Index = clamp(NewVal-1, -1, pEditor->m_Map.m_lEnvelopes.size()-1);
+		int Step = (Index-pQuad->m_ColorEnv)%2;
+		if(Step != 0)
+		{
+			for(; Index >= -1 && Index < pEditor->m_Map.m_lEnvelopes.size(); Index += Step)
+				if(Index == -1 || pEditor->m_Map.m_lEnvelopes[Index]->m_Channels == 4)
+				{
+					pQuad->m_ColorEnv = Index;
+					break;
+				}
+		}
+	}
 	if(Prop == PROP_COLOR_ENV_OFFSET) pQuad->m_ColorEnvOffset = NewVal;
 
 	return 0;
@@ -528,8 +584,8 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View)
 		View.HSplitBottom(40.0f, &View, 0);
 		View.VMargin(40.0f, &View);
 		View.HSplitBottom(20.0f, &View, &Label);
-		static int s_FolderBox = 0;
-		pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_FileDialogNewFolderName, sizeof(pEditor->m_FileDialogNewFolderName), 15.0f);
+		static float s_FolderBox = 0;
+		pEditor->DoEditBox(&s_FolderBox, &Label, pEditor->m_FileDialogNewFolderName, sizeof(pEditor->m_FileDialogNewFolderName), 15.0f, &s_FolderBox);
 		View.HSplitBottom(20.0f, &View, &Label);
 		pEditor->UI()->DoLabel(&Label, "Name:", 10.0f, -1);
 
@@ -579,6 +635,74 @@ int CEditor::PopupNewFolder(CEditor *pEditor, CUIRect View)
 	return 0;
 }
 
+int CEditor::PopupMapInfo(CEditor *pEditor, CUIRect View)
+{
+	CUIRect Label, ButtonBar, Button;
+
+	// title
+	View.HSplitTop(10.0f, 0, &View);
+	View.HSplitTop(30.0f, &Label, &View);
+	pEditor->UI()->DoLabel(&Label, "Map details", 20.0f, 0);
+
+	View.HSplitBottom(10.0f, &View, 0);
+	View.HSplitBottom(20.0f, &View, &ButtonBar);
+
+	View.VMargin(40.0f, &View);
+
+	// author box
+	View.HSplitTop(20.0f, &Label, &View);
+	pEditor->UI()->DoLabel(&Label, "Author:", 10.0f, -1);
+	Label.VSplitLeft(40.0f, 0, &Button);
+	Button.HSplitTop(12.0f, &Button, 0);
+	static float s_AuthorBox = 0;
+	pEditor->DoEditBox(&s_AuthorBox, &Button, pEditor->m_Map.m_MapInfo.m_aAuthorTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aAuthorTmp), 10.0f, &s_AuthorBox);
+
+	// version box
+	View.HSplitTop(20.0f, &Label, &View);
+	pEditor->UI()->DoLabel(&Label, "Version:", 10.0f, -1);
+	Label.VSplitLeft(40.0f, 0, &Button);
+	Button.HSplitTop(12.0f, &Button, 0);
+	static float s_VersionBox = 0;
+	pEditor->DoEditBox(&s_VersionBox, &Button, pEditor->m_Map.m_MapInfo.m_aVersionTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aVersionTmp), 10.0f, &s_VersionBox);
+
+	// credits box
+	View.HSplitTop(20.0f, &Label, &View);
+	pEditor->UI()->DoLabel(&Label, "Credits:", 10.0f, -1);
+	Label.VSplitLeft(40.0f, 0, &Button);
+	Button.HSplitTop(12.0f, &Button, 0);
+	static float s_CreditsBox = 0;
+	pEditor->DoEditBox(&s_CreditsBox, &Button, pEditor->m_Map.m_MapInfo.m_aCreditsTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aCreditsTmp), 10.0f, &s_CreditsBox);
+
+	// license box
+	View.HSplitTop(20.0f, &Label, &View);
+	pEditor->UI()->DoLabel(&Label, "License:", 10.0f, -1);
+	Label.VSplitLeft(40.0f, 0, &Button);
+	Button.HSplitTop(12.0f, &Button, 0);
+	static float s_LicenseBox = 0;
+	pEditor->DoEditBox(&s_LicenseBox, &Button, pEditor->m_Map.m_MapInfo.m_aLicenseTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aLicenseTmp), 10.0f, &s_LicenseBox);
+
+	// button bar
+	ButtonBar.VSplitLeft(30.0f, 0, &ButtonBar);
+	ButtonBar.VSplitLeft(110.0f, &Label, &ButtonBar);
+	static int s_CreateButton = 0;
+	if(pEditor->DoButton_Editor(&s_CreateButton, "Save", 0, &Label, 0, 0))
+	{
+		str_copy(pEditor->m_Map.m_MapInfo.m_aAuthor, pEditor->m_Map.m_MapInfo.m_aAuthorTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aAuthor));
+		str_copy(pEditor->m_Map.m_MapInfo.m_aVersion, pEditor->m_Map.m_MapInfo.m_aVersionTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aVersion));
+		str_copy(pEditor->m_Map.m_MapInfo.m_aCredits, pEditor->m_Map.m_MapInfo.m_aCreditsTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aCredits));
+		str_copy(pEditor->m_Map.m_MapInfo.m_aLicense, pEditor->m_Map.m_MapInfo.m_aLicenseTmp, sizeof(pEditor->m_Map.m_MapInfo.m_aLicense));
+		return 1;
+	}
+
+	ButtonBar.VSplitRight(30.0f, &ButtonBar, 0);
+	ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label);
+	static int s_AbortButton = 0;
+	if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, 0))
+		return 1;
+
+	return 0;
+}
+
 int CEditor::PopupEvent(CEditor *pEditor, CUIRect View)
 {
 	CUIRect Label, ButtonBar;
@@ -628,13 +752,17 @@ int CEditor::PopupEvent(CEditor *pEditor, CUIRect View)
 		}
 		else if(pEditor->m_PopupEventType == POPEVENT_SAVE)
 			pEditor->CallbackSaveMap(pEditor->m_aFileSaveName, IStorage::TYPE_SAVE, pEditor);
+		pEditor->m_PopupEventWasActivated = false;
 		return 1;
 	}
 	ButtonBar.VSplitRight(30.0f, &ButtonBar, 0);
 	ButtonBar.VSplitRight(110.0f, &ButtonBar, &Label);
 	static int s_AbortButton = 0;
 	if(pEditor->DoButton_Editor(&s_AbortButton, "Abort", 0, &Label, 0, 0))
+	{
+		pEditor->m_PopupEventWasActivated = false;
 		return 1;
+	}
 
 	return 0;
 }
@@ -673,13 +801,23 @@ int CEditor::PopupSelectImage(CEditor *pEditor, CUIRect View)
 	}
 
 	if(ShowImage >= 0 && ShowImage < pEditor->m_Map.m_lImages.size())
+	{
+		if(ImageView.h < ImageView.w)
+			ImageView.w = ImageView.h;
+		else
+			ImageView.h = ImageView.w;
+		float Max = (float)(max(pEditor->m_Map.m_lImages[ShowImage]->m_Width, pEditor->m_Map.m_lImages[ShowImage]->m_Height));
+		ImageView.w *= pEditor->m_Map.m_lImages[ShowImage]->m_Width/Max;
+		ImageView.h *= pEditor->m_Map.m_lImages[ShowImage]->m_Height/Max;
 		pEditor->Graphics()->TextureSet(pEditor->m_Map.m_lImages[ShowImage]->m_TexID);
-	else
-		pEditor->Graphics()->TextureSet(-1);
-	pEditor->Graphics()->QuadsBegin();
-	IGraphics::CQuadItem QuadItem(ImageView.x, ImageView.y, ImageView.w, ImageView.h);
-	pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1);
-	pEditor->Graphics()->QuadsEnd();
+		pEditor->Graphics()->BlendNormal();
+		pEditor->Graphics()->WrapClamp();
+		pEditor->Graphics()->QuadsBegin();
+		IGraphics::CQuadItem QuadItem(ImageView.x, ImageView.y, ImageView.w, ImageView.h);
+		pEditor->Graphics()->QuadsDrawTL(&QuadItem, 1);
+		pEditor->Graphics()->QuadsEnd();
+		pEditor->Graphics()->WrapNormal();
+	}
 
 	return 0;
 }
@@ -737,3 +875,43 @@ int CEditor::PopupSelectGameTileOpResult()
 	s_GametileOpSelected = -1;
 	return Result;
 }
+
+static int s_AutoMapConfigSelected = -1;
+
+int CEditor::PopupSelectConfigAutoMap(CEditor *pEditor, CUIRect View)
+{
+	CLayerTiles *pLayer = static_cast<CLayerTiles*>(pEditor->GetSelectedLayer(0));
+	CUIRect Button;
+	static int s_AutoMapperConfigButtons[256];
+	CAutoMapper *pAutoMapper = &pEditor->m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper;
+
+	for(int i = 0; i < pAutoMapper->ConfigNamesNum(); ++i)
+	{
+		View.HSplitTop(2.0f, 0, &View);
+		View.HSplitTop(12.0f, &Button, &View);
+		if(pEditor->DoButton_Editor(&s_AutoMapperConfigButtons[i], pAutoMapper->GetConfigName(i), 0, &Button, 0, 0))
+			s_AutoMapConfigSelected = i;
+	}
+
+	return 0;
+}
+
+void CEditor::PopupSelectConfigAutoMapInvoke(float x, float y)
+{
+	static int s_AutoMapConfigSelectID = 0;
+	s_AutoMapConfigSelected = -1;
+	CLayerTiles *pLayer = static_cast<CLayerTiles*>(GetSelectedLayer(0));
+	if(pLayer && pLayer->m_Image >= 0 && pLayer->m_Image < m_Map.m_lImages.size() &&
+		m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum())
+		UiInvokePopupMenu(&s_AutoMapConfigSelectID, 0, x, y, 120.0f, 12.0f+14.0f*m_Map.m_lImages[pLayer->m_Image]->m_AutoMapper.ConfigNamesNum(), PopupSelectConfigAutoMap);
+}
+
+int CEditor::PopupSelectConfigAutoMapResult()
+{
+	if(s_AutoMapConfigSelected < 0)
+		return -1;
+
+	int Result = s_AutoMapConfigSelected;
+	s_AutoMapConfigSelected = -1;
+	return Result;
+}