1 /*
2  * Copyright (c) 2017-2018 sel-project
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a copy
5  * of this software and associated documentation files (the "Software"), to deal
6  * in the Software without restriction, including without limitation the rights
7  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  * copies of the Software, and to permit persons to whom the Software is
9  * furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in all
12  * copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20  * SOFTWARE.
21  *
22  */
23 /**
24  * Copyright: 2017-2018 sel-project
25  * License: MIT
26  * Authors: Kripth
27  * Source: $(HTTP github.com/sel-project/selery/source/selery/world/map.d, selery/world/map.d)
28  */
29 module selery.world.map;
30 
31 import std.conv : to;
32 import std.math : log2, floor, abs;
33 import std.traits : EnumMembers;
34 
35 import selery.math.vector : ChunkPosition;
36 import selery.util.color;
37 import selery.world.world : World;
38 
39 class Map {
40 
41 	public static immutable ubyte ONE_BLOCK_PER_PIXEL = 0;
42 	public static immutable ubyte TWO_BLOCKS_PER_PIXEL = 1;
43 	public static immutable ubyte FOUR_BLOCKS_PER_PIXEL = 2;
44 	public static immutable ubyte EIGHT_BLOCKS_PER_PIXEL = 3;
45 	public static immutable ubyte SIXTEEN_BLOCKS_PER_PIXEL = 4;
46 
47 	private World world;
48 
49 	public immutable ushort id;
50 
51 	private ubyte n_scale;
52 	private MapColor[] m_data;
53 
54 	public @safe this(World world, ushort id, ubyte scale=0, MapColor[] data=null) {
55 		this.world = world;
56 		this.id = id;
57 		this.scale = scale;
58 		if(data !is null) this.data = data;
59 	}
60 
61 	public @property @safe @nogc ubyte scale() {
62 		return this.n_scale;
63 	}
64 
65 	public @property @safe ubyte scale(ubyte scale) {
66 		assert(scale <= 4, "Invalid scale given");
67 		return this.n_scale = scale;
68 	}
69 
70 	public @property @safe @nogc MapColor[] data() {
71 		return this.m_data;
72 	}
73 
74 	public @property @safe MapColor[] data(MapColor[] data) {
75 		assert(data.length == 128 * 128, "wrong buffer length");
76 		this.m_data = data;
77 		//this.world.compress(this);
78 		return this.m_data;
79 	}
80 
81 	public @safe MapColor opIndexAssign(uint x, uint z) {
82 		return this.m_data[x + z * 128];
83 	}
84 
85 	public @safe void opIndexAssign(MapColor color, uint x, uint z) {
86 		this.m_data[x + z * 128] = color;
87 	}
88 
89 	/*protected @property @safe ubyte[] pedata() {
90 		ubyte[] buffer;
91 		buffer.reserve(128 * 128 * 4);
92 		foreach(ref MapColor color ; this.m_data) {
93 			buffer ~= color.r;
94 			buffer ~= color.g;
95 			buffer ~= color.b;
96 			buffer ~= 255;
97 		}
98 		return buffer;
99 	}
100 
101 	protected @property @safe ubyte[] pcdata() {
102 		ubyte[] buffer;
103 		buffer.reserve(128 * 128);
104 		foreach(ref MapColor color ; this.m_data) {
105 			buffer ~= color.id;
106 		}
107 		return buffer;
108 	}*/
109 
110 	public enum Colors : MapColors {
111 
112 		TRANSPARENT = MapColors(0, 0, 0, 0),
113 		GRASS = MapColors(1, 127, 178, 56),
114 		SAND = MapColors(2, 244, 230, 161),
115 		BED = MapColors(3, 199, 199, 199),
116 		LAVA = MapColors(4, 252, 0, 0),
117 		ICE = MapColors(5, 158, 158, 252),
118 		IRON = MapColors(6, 165, 165, 165),
119 		LEAVES = MapColors(7, 0, 123, 0),
120 		SNOW = MapColors(8, 252, 252, 252),
121 		CLAY = MapColors(9, 162, 166, 182),
122 		DIRT = MapColors(10, 149, 108, 76),
123 		STONE = MapColors(11, 111, 111, 111),
124 		WATER = MapColors(12, 63, 63, 252),
125 		WOOD = MapColors(13, 141, 118, 71),
126 		QUARTZ = MapColors(14, 252, 249, 242),
127 		ORANGE = MapColors(15, 213, 125, 50),
128 		MAGENTA = MapColors(16, 176, 75, 213),
129 		LIGHT_BLUE = MapColors(17, 101, 151, 213),
130 		YELLOW = MapColors(18, 226, 226, 50),
131 		LIME = MapColors(19, 125, 202, 25),
132 		PINK = MapColors(20, 239, 125, 163),
133 		GRAY = MapColors(21, 75, 75, 75),
134 		LIGHT_GRAY = MapColors(22, 151, 151, 151),
135 		CYAN = MapColors(23, 75, 125, 151),
136 		PURPLE = MapColors(24, 125, 62, 176),
137 		BLUE = MapColors(25, 50, 75, 176),
138 		BROWN = MapColors(26, 101, 75, 50),
139 		GREEN = MapColors(27, 101, 125, 50),
140 		RED = MapColors(28, 151, 50, 50),
141 		BLACK = MapColors(29, 25, 25, 25),
142 		GOLD = MapColors(30, 247, 235, 76),
143 		DIAMOND = MapColors(31, 91, 216, 210),
144 		LAPIS_LAZULI = MapColors(32, 73, 129, 252),
145 		EMERALD = MapColors(33, 0, 214, 57),
146 		PODZOL = MapColors(34, 127, 85, 48),
147 		NETHERRACK = MapColors(35, 111, 2, 0),
148 
149 	}
150 
151 	public static MapColor closestColor(Color color) {
152 		if(color.a != 255) return Colors.TRANSPARENT.light;
153 		uint difference = uint.max;
154 		MapColor ret;
155 		foreach(MapColors mc ; [EnumMembers!Colors]) {
156 			if(mc[0].id == 0) continue;
157 			foreach(const MapColor c ; mc) {
158 				uint d = 0;
159 				d += abs(to!int(c.r) - color.r);
160 				d += abs(to!int(c.g) - color.g);
161 				d += abs(to!int(c.b) - color.b);
162 				if(d < difference) {
163 					difference = d;
164 					ret = cast()c;
165 				}
166 			}
167 		}
168 		return ret;
169 	}
170 
171 }
172 
173 class CustomMap : Map {
174 
175 	private ubyte[] image_data;
176 
177 	public this(World world, ushort id, ubyte scale, ubyte[] image_data) {
178 		assert(image_data.length == 128 * 128 * 4, "Invalid image data length");
179 		super(world, id, scale);
180 		this.image_data = image_data;
181 		MapColor[] m;
182 		m.reserve(128 * 128);
183 		foreach(uint i ; 0..128*128) {
184 			uint j = i * 4;
185 			m ~= Map.closestColor(new Color(image_data[j++], image_data[j++], image_data[j++], image_data[j]));
186 		}
187 		this.data = m;
188 	}
189 
190 	/*protected override @property @safe ubyte[] pedata() {
191 		return this.image_data;
192 	}*/
193 
194 }
195 
196 struct MapColors {
197 
198 	public MapColor[4] colors;
199 
200 	public @safe this(ubyte id, uint r, uint g, uint b) {
201 		id *= 4;
202 		this.colors[0] = new MapColor(id++, m(r, 180), m(g, 180), m(b, 180));
203 		this.colors[1] = new MapColor(id++, m(r, 220), m(g, 220), m(b, 220));
204 		this.colors[2] = new MapColor(id++, r & 255, g & 255, b & 255);
205 		this.colors[3] = new MapColor(id++, m(r, 135), m(g, 135), m(b, 135));
206 	}
207 
208 	private @safe ubyte m(uint num, uint amount) {
209 		return (num * amount / 255) & 255;
210 	}
211 
212 	public @property @safe MapColor light() {
213 		return this.colors[2];
214 	}
215 
216 	public @property @safe MapColor normal() {
217 		return this.colors[1];
218 	}
219 
220 	public @property @safe MapColor dark() {
221 		return this.colors[0];
222 	}
223 
224 	public @property @safe MapColor veryDark() {
225 		return this.colors[3];
226 	}
227 
228 	alias colors this;
229 
230 }
231 
232 class MapColor : Color {
233 
234 	public immutable ubyte id;
235 
236 	public @safe @nogc this(ubyte id, ubyte r, ubyte g, ubyte b) {
237 		super(r, g, b);
238 		this.id = id;
239 	}
240 
241 }