1 module document;
2 
3 import PixelPerfectEngine.map.mapdata;
4 import PixelPerfectEngine.map.mapformat;
5 import editorEvents;
6 import rasterWindow;
7 import PixelPerfectEngine.concrete.eventChainSystem;
8 import PixelPerfectEngine.graphics.common : Color, Coordinate;
9 import PixelPerfectEngine.graphics.bitmap;
10 import PixelPerfectEngine.graphics.layers;
11 import PixelPerfectEngine.system.inputHandler : MouseButton, ButtonState;
12 import std.stdio;
13 
14 import app;
15 ///Individual document for parallel editing
16 public class MapDocument {
17 	/**
18 	 * Specifies the current edit mode
19 	 */
20 	public enum EditMode {
21 		selectDragScroll,
22 		tilePlacement,
23 		boxPlacement,
24 		spritePlacement,
25 	}
26 	UndoableStack		events;		///Per document event stack
27 	MapFormat			mainDoc;	///Used to reduce duplicate data as much as possible
28 	//ABitmap[] delegate() imageReturnFunc;
29 	Color[] delegate(MapDocument sender) paletteReturnFunc;	///Used for adding the palette for the document
30 	int					selectedLayer;	///Indicates the currently selected layer
31 	RasterWindow		outputWindow;	///Window used to output the screen data
32 	EditMode			mode;			///Mose event mode selector
33 	protected int		prevMouseX;		///Previous mouse X position
34 	protected int		prevMouseY;		///Previous mouse Y position
35 	protected MappingElement	selectedMappingElement;	///Currently selected mapping element to write, including mirroring properties, palette selection, and priority attributes
36 	protected bool		voidfill;		///If true, tilePlacement overrides only transparent (0xFFFF) tiles.
37 	/**
38 	 * Loads the document from disk.
39 	 */
40 	public this(string filename) @trusted {
41 		events = new UndoableStack(20);
42 	}
43 	///New from scratch
44 	public this(string docName, int resX, int resY) @trusted {
45 		events = new UndoableStack(20);
46 		mainDoc = new MapFormat(docName, resX, resY);
47 		mode = EditMode.tilePlacement;
48 	}
49 	///Returns the next available layer number.
50 	public int nextLayerNumber() @safe {
51 		int result = selectedLayer;
52 		bool found;
53 		do {
54 			if (mainDoc[result] is null)
55 				found = true;
56 			else
57 				result++;
58 		} while (!found);
59 		return result;
60 	}
61 	///Puts the loaded tiles onto a TileLayer
62 	public void addTileSet(int layer, ABitmap[ushort] tiles) @trusted {
63 		ITileLayer itl = cast(ITileLayer)mainDoc[layer];
64 		foreach (i ; tiles.byKey) {
65 			itl.addTile(tiles[i], i);
66 		}
67 	}
68 	///Ditto
69 	public void addTileSet(int layer, ABitmap[] tiles) @trusted {
70 		ITileLayer itl = cast(ITileLayer)mainDoc[layer];
71 		for (ushort i ; i < tiles.length ; i++) {
72 			itl.addTile(tiles[i], i);
73 		}
74 	}
75 	/**
76 	 * Pass mouse events here.
77 	 */
78 	public void passMouseEvent(int x, int y, int state, ubyte button) {
79 		//Normal mode:
80 		//left : drag layer/select ; right : menu ; middle : quick nav ; other buttons : user defined
81 		//TileLayer placement mode:
82 		//left : placement ; right : menu ; middle : delete ; other buttons : user defined
83 		final switch (mode) {
84 			case EditMode.selectDragScroll:
85 				switch (button) {
86 					case MouseButton.LEFT:
87 						//Test if an object is being hit by the cursor. If yes, then select the object. If not, then initialize drag layer mode.
88 						break;
89 					case MouseButton.MID:
90 						//Enable quicknav mode. Scroll the layer by delta/10 for each frame. Stop if button is released.
91 						break;
92 					default:
93 						break;
94 				}
95 				break;
96 			case EditMode.tilePlacement:
97 
98 				switch (button) {
99 					case MouseButton.LEFT:
100 						//Record the first cursor position upon mouse button press, then initialize either a single or zone write for the selected tile layer.
101 						if (state == ButtonState.PRESSED) {
102 							prevMouseX = x;
103 							prevMouseY = y;
104 						} else {
105 
106 							ITileLayer target = cast(ITileLayer)(mainDoc[selectedLayer]);
107 							x = (x - mainDoc[selectedLayer].getSX) / target.getTileWidth;
108 							y = (y - mainDoc[selectedLayer].getSY) / target.getTileHeight;
109 							prevMouseX = (prevMouseX - mainDoc[selectedLayer].getSX) / target.getTileWidth;
110 							prevMouseY = (prevMouseY - mainDoc[selectedLayer].getSY) / target.getTileHeight;
111 							Coordinate c;
112 							if (x > prevMouseX){
113 								c.left = prevMouseX;
114 								c.right = x;
115 							} else {
116 								c.left = x;
117 								c.right = prevMouseX;
118 							}
119 							if (y > prevMouseY){
120 								c.top = prevMouseY;
121 								c.bottom = y;
122 							} else {
123 								c.top = y;
124 								c.bottom = prevMouseY;
125 							}
126 
127 							if (voidfill) {
128 								if (c.width == 0 && c.height == 0) {
129 									if (target.readMapping(c.left, c.top).tileID == 0xFFFF)
130 										events.addToTop(new WriteToMapSingle(target, c.left, c.top, selectedMappingElement));
131 									/+	target.writeMapping(c.left, c.top, selectedMappingElement);+/
132 
133 								} else {
134 									/+for (int y0 = c.top ; y0 <= c.bottom ; y0++){
135 										for (int x0 = c.left ; x0 <= c.right ; x0++) {
136 											if (target.readMapping(x0, y0).tileID == 0xFFFF)
137 												target.writeMapping(x0, y0, selectedMappingElement);
138 										}
139 									}+/
140 									events.addToTop(new WriteToMapVoidFill(target, c, selectedMappingElement));
141 								}
142 							} else {
143 								if (c.width == 0 && c.height == 0) {
144 									events.addToTop(new WriteToMapSingle(target, c.left, c.top, selectedMappingElement));
145 
146 								} else {
147 									events.addToTop(new WriteToMapOverwrite(target, c, selectedMappingElement));
148 								}
149 							}
150 							debug {
151 								import std.stdio : writeln;
152 								writeln(events.events);
153 							}
154 						}
155 						break;
156 					case MouseButton.MID:
157 						//Record the first cursor position upon mouse button press, then initialize either a single or zone delete for the selected tile layer.
158 						if (state == ButtonState.PRESSED) {
159 							prevMouseX = x;
160 							prevMouseY = y;
161 						} else {
162 
163 						}
164 						break;
165 					case MouseButton.RIGHT:
166 						//Open quick menu with basic edit options and ability of toggling both vertically and horizontally.
167 						break;
168 					default:
169 						break;
170 				}
171 				outputWindow.draw();
172 				//outputWindow.updateRaster();
173 				break;
174 			case EditMode.spritePlacement:
175 				break;
176 			case EditMode.boxPlacement:
177 				break;
178 		}
179 	}
180 	public void updateMaterialList () {
181 		if (mainDoc[selectedLayer] !is null) {
182 			if (prg.wh.materialList !is null) {
183 				TileInfo[] list = mainDoc.getTileInfo(selectedLayer);
184 				//writeln(list.length);
185 				prg.wh.materialList.updateMaterialList(list);
186 			}
187 		}
188 	}
189 	public void updateLayerList () {
190 		if (prg.wh.layerList !is null) {
191 			LayerInfo[] list = mainDoc.getLayerInfo;
192 			prg.wh.layerList.updateLayerList(list);
193 			//prg.wh.layerlist
194 		}
195 	}
196 	public void onSelection () {
197 		updateLayerList;
198 		updateMaterialList;
199 	}
200 	public void tileMaterial_FlipHorizontal() {
201 		selectedMappingElement.attributes.horizMirror = !selectedMappingElement.attributes.horizMirror;
202 	}
203 	public void tileMaterial_FlipVertical() {
204 		selectedMappingElement.attributes.vertMirror = !selectedMappingElement.attributes.vertMirror;
205 	}
206 	public void tileMaterial_Select(wchar id) {
207 		selectedMappingElement.tileID = id;
208 		mode = EditMode.tilePlacement;
209 
210 	}
211 	public void tileMaterial_PaletteUp() {
212 		selectedMappingElement.paletteSel++;
213 	}
214 	public void tileMaterial_PaletteDown() {
215 		selectedMappingElement.paletteSel--;
216 	}
217 }