001package squidpony.squidgrid.gui.gdx;
002
003import com.badlogic.gdx.graphics.Color;
004import squidpony.ArrayTools;
005import squidpony.squidgrid.gui.gdx.ICellVisible.Basic;
006import squidpony.squidgrid.mapping.WildMap;
007import squidpony.squidgrid.mapping.WorldMapGenerator;
008import squidpony.squidmath.IntPointHash;
009import squidpony.squidmath.SilkRNG;
010
011import java.util.HashMap;
012import java.util.Map;
013
014import static squidpony.squidgrid.gui.gdx.SColor.*;
015import static squidpony.squidgrid.gui.gdx.WorldMapView.*;
016
017/**
018 * Created by Tommy Ettinger on 9/6/2019.
019 */
020public class WildMapView {
021    protected int width, height;
022    protected float[][] colorMap;
023    public WildMap wildMap;
024    
025    public Map<String, ? extends ICellVisible> viewer;
026    
027    public int getWidth() {
028        return width;
029    }
030
031    public int getHeight() {
032        return height;
033    }
034
035    public float[][] getColorMap() {
036        return colorMap;
037    }
038    
039    public WildMap getWildMap() {
040        return wildMap;
041    }
042
043    public void setWildMap(WildMap wildMap) {
044        this.wildMap = wildMap;
045        if(this.width != wildMap.width || this.height != wildMap.height)
046        {
047            width = wildMap.width;
048            height = wildMap.height;
049            colorMap = new float[width][height];
050        }
051    }
052    
053    public static HashMap<String, ? extends ICellVisible> defaultViewer()
054    {
055        HashMap<String, ICellVisible> viewer = new HashMap<>(128);
056
057        viewer.put("snow path", new Basic('.', ALICE_BLUE.toEditedFloat(0.0f, -0.2f, -0.15f)));
058        viewer.put("dirt path", new Basic('.', CLOVE_BROWN.toEditedFloat(-0.005f, -0.275f, 0.17f)));
059        viewer.put("sand path", new Basic('.', CW_PALE_ORANGE.toEditedFloat(0.05f, -0.17f, -0.075f)));
060        viewer.put("grass path", new Basic('.', AURORA_DUSTY_GREEN.toEditedFloat(0.0f, -0.15f, -0.1f)));
061        viewer.put("stone path", new Basic('.', AURORA_CHIPPED_GRANITE.toEditedFloat(-0.09f, -0.05f, 0.1f)));
062        viewer.put("wooden bridge", new Basic(':', BRUSHWOOD_DYED.toEditedFloat(0.0f, -0.275f, 0.05f)));
063
064        viewer.put("ice ledge", new Basic('¬', SColor.toEditedFloat(PALE_CORNFLOWER_BLUE, 0.0f, -0.1f, 0.1f)));
065        viewer.put("dirt ledge", new Basic('¬', CLOVE_BROWN.toEditedFloat(-0.005f, -0.175f, -0.18f)));
066        viewer.put("sand ledge", new Basic('¬', CW_PALE_ORANGE.toEditedFloat(0.05f, -0.15f, -0.125f)));
067        viewer.put("grass ledge", new Basic('¬', AURORA_DUSTY_GREEN.toEditedFloat(0.0f, -0.025f, -0.45f)));
068        viewer.put("stone ledge", new Basic('¬', AURORA_CHIPPED_GRANITE.toEditedFloat(-0.07f, -0.1f, -0.25f)));
069
070        viewer.put("snow", new Basic('…', ALICE_BLUE));
071        viewer.put("ice", new Basic('-', SColor.lightenFloat(PALE_CORNFLOWER_BLUE, 0.3f)));
072        viewer.put("dirt", new Basic('·', CLOVE_BROWN.toEditedFloat(-0.005f, -0.075f, 0.02f)));
073        viewer.put("pebbles", new Basic('…', AURORA_WET_STONE.toEditedFloat(0.0f, 0.0f, 0.0f)));
074        viewer.put("dry grass", new Basic('\'', CW_FADED_BROWN.toEditedFloat(0.06f, 0.05f, 0.05f)));
075        viewer.put("fresh water", new Basic('~', AURORA_BLUE_EYE));
076        viewer.put("salt water", new Basic('≈', AURORA_PRUSSIAN_BLUE));
077        viewer.put("sand", new Basic('…', CW_PALE_ORANGE.toEditedFloat(0.05f, -0.05f, 0.075f)));
078        viewer.put("leaves", new Basic('…', CHINESE_TEA_YELLOW.toEditedFloat(0.02f, -0.025f, 0.0f)));
079        viewer.put("grass", new Basic('"', AURORA_DUSTY_GREEN.toEditedFloat(0.0f, 0.075f, -0.25f)));
080        viewer.put("mud", new Basic(',', DB_EARTH.toEditedFloat(0.03f, -0.15f, -0.03f)));
081        viewer.put("moss", new Basic('˝', AURORA_FERN_GREEN.toEditedFloat(0f, 0.0f, 0.0f)));
082        viewer.put("rubble", new Basic('‰', AURORA_CHIPPED_GRANITE.toEditedFloat(-0.07f, 0.0f, -0.05f)));
083        viewer.put("empty space", new Basic('_', DB_INK));
084        viewer.put("snow mound", new Basic('∆', ALICE_BLUE.toEditedFloat(0f, 0.05f, -0.1f)));
085        viewer.put("icy divot", new Basic('°', ALICE_BLUE.toEditedFloat(0.05f, 0.075f, 0.06f)));
086        viewer.put("powder snowdrift", new Basic('¨', ALICE_BLUE.toEditedFloat(0.0f, 0.0f, -0.07f)));
087        viewer.put("hillock", new Basic('∆', CW_DRAB_BROWN.toEditedFloat(0.1f, -0.05f, 0.25f)));
088        viewer.put("animal burrow", new Basic('¸', AURORA_ARMY_GREEN.toEditedFloat(0.05f, 0.0f, -0.05f)));
089        viewer.put("small bush 1", new Basic('♣', AURORA_AVOCADO.toEditedFloat(-0.055f, -0.025f, -0.225f)));
090        viewer.put("large bush 1", new Basic('♣', AURORA_FOREST_GLEN.toEditedFloat(-0.055f, -0.125f, -0.225f)));
091        viewer.put("evergreen tree 1", new Basic('♠', PINE_GREEN.toEditedFloat(-0.13f, -0.03f, -0.05f)));
092        viewer.put("evergreen tree 2", new Basic('♠', AURORA_EUCALYPTUS.toEditedFloat(-0.035f, -0.045f, -0.75f)));
093        viewer.put("small cactus 1", new Basic('‡', AURORA_FROG_GREEN.toEditedFloat(0.035f, 0.065f, -0.06f)));
094        viewer.put("large cactus 1", new Basic('‡', AURORA_MARSH.toEditedFloat(0.04f, 0.11f, -0.03f)));
095        viewer.put("succulent 1", new Basic('§', CW_FLUSH_JADE.toEditedFloat(-0.045f, -0.1f, 0.0f)));
096        viewer.put("seashell 1", new Basic('ˋ', CW_LIGHT_APRICOT.toEditedFloat(0.0f, -0.095f, 0.07f)));
097        viewer.put("seashell 2", new Basic('ˋ', CW_PALE_RED.toEditedFloat(0.0f, -0.2f, 0.1f)));
098        viewer.put("seashell 3", new Basic('ˋ', CW_PALE_YELLOW.toEditedFloat(0.0f, 0.02f, 0.05f)));
099        viewer.put("seashell 4", new Basic('ˋ', CW_PALE_VIOLET.toEditedFloat(0.0f, -0.080f, 0.11f)));
100        viewer.put("driftwood", new Basic('¿', AURORA_DRIFTWOOD.toEditedFloat(0.0f, -0.25f, 0.04f)));
101        viewer.put("boulder", new Basic('●', AURORA_SLOW_CREEK.toEditedFloat(0.0f, -0.01f, 0.0f)));
102        viewer.put("deciduous tree 1", new Basic('¥', AURORA_AVOCADO.toEditedFloat(-0.065f, 0.0f, -0.3f)));
103        viewer.put("small bush 2", new Basic('♣', AURORA_WOODLANDS.toEditedFloat(-0.045f, -0.05f, -0.025f)));
104        viewer.put("deciduous tree 2", new Basic('¥', AURORA_IVY_GREEN.toEditedFloat(-0.02f, 0.0f, 0.0f)));
105        viewer.put("deciduous tree 3", new Basic('¥', AURORA_ASPARAGUS.toEditedFloat(-0.015f, 0.055f, 0.02f)));
106        viewer.put("large bush 2", new Basic('♣', AURORA_VIRIDIAN.toEditedFloat(-0.03f, -0.05f, 0.03f)));
107        viewer.put("tropical tree 1", new Basic('¶', AURORA_FLORAL_FOAM.toEditedFloat(-0.05f, 0.025f, 0.075f)));
108        viewer.put("tropical tree 2", new Basic('¶', AURORA_MAIDENHAIR_FERN.toEditedFloat(0.0f, 0.0f, 0.02f)));
109        viewer.put("large bush 3", new Basic('♣', AURORA_KELLY_GREEN.toEditedFloat(0.0f, 0.025f, 0.02f)));
110        viewer.put("tropical tree 3", new Basic('¶', AURORA_SOFT_TEAL.toEditedFloat(-0.15f, -0.07f, -0.03f)));
111        viewer.put("tropical tree 4", new Basic('¶', AURORA_PRASE.toEditedFloat(-0.04f, -0.02f, -0.02f)));
112        return viewer;
113    }
114
115    public WildMapView()
116    {
117        this(null);
118    }
119    public WildMapView(WildMap wildMap)
120    {
121        if(wildMap == null)
122        {
123            this.wildMap = new WildMap();
124        }
125        else
126        {
127            this.wildMap = wildMap;
128        }
129        this.viewer = defaultViewer();
130        width = this.wildMap.width;
131        height = this.wildMap.height;
132        colorMap = new float[width][height];
133        initialize();
134    }
135    public WildMapView(WildMap wildMap, Map<String, ? extends ICellVisible> viewer)
136    {
137        if(wildMap == null) { // default to forest map, biome 21; ignore given viewer, it may be null
138            this.wildMap = new WildMap();
139            this.viewer = defaultViewer();
140        }
141        else
142        {
143            this.wildMap = wildMap;
144            this.viewer = viewer == null ? defaultViewer() : viewer;
145        }
146        width = this.wildMap.width;
147        height = this.wildMap.height;
148        colorMap = new float[width][height];
149        initialize();
150    }
151    
152    public WildMapView(long seed, int width, int height, int biome)
153    {
154        this(new WildMap(width, height, biome, new SilkRNG(seed)));
155    }
156    
157    protected float[] biomeColors = {
158            desertColor,
159            savannaColor,
160            tropicalRainforestColor,
161            grasslandColor,
162            woodlandColor,
163            seasonalForestColor,
164            temperateRainforestColor,
165            borealForestColor,
166            tundraColor,
167            iceColor,
168            beachColor,
169            rockyColor,
170            shallowColor,
171            deepColor,
172            emptyColor
173    };
174    public final float[] BIOME_COLOR_TABLE = new float[61], BIOME_DARK_COLOR_TABLE = new float[61];
175
176    public void initialize()
177    {
178        initialize(0f, 0f, 0f, 1f);
179    }
180
181    public void initialize(float hue, float saturation, float brightness, float contrast)
182    {
183        float b, diff;
184        for (int i = 0; i < 60; i++) {
185            b = BIOME_TABLE[i];
186            diff = (b % 1.0f - 0.48f) * 0.27f * contrast;
187            BIOME_COLOR_TABLE[i] = b = SColor.toEditedFloat(diff >= 0
188                    ? SColor.lightenFloat(biomeColors[(int)b], diff)
189                    : SColor.darkenFloat(biomeColors[(int)b], -diff), hue, saturation, brightness, 0f);
190            BIOME_DARK_COLOR_TABLE[i] = SColor.darkenFloat(b, 0.08f);
191        }
192        BIOME_COLOR_TABLE[60] = BIOME_DARK_COLOR_TABLE[60] = emptyColor;
193    }
194
195    /**
196     * Initializes the colors to use for each biome (these are almost always mixed with other biome colors in practice).
197     * Each parameter may be null to use the default for an Earth-like world; otherwise it should be a libGDX
198     * {@link Color} or some subclass, like {@link SColor}. All non-null parameters should probably be fully opaque,
199     * except {@code emptyColor}, which is only used for world maps that show empty space (like a globe, as produced by
200     * {@link WorldMapGenerator.RotatingSpaceMap}).
201     * @param desertColor hot, dry, barren land; may be sandy, but many real-world deserts don't have much sand
202     * @param savannaColor hot, mostly-dry land with some parched vegetation; also called scrub or chaparral
203     * @param tropicalRainforestColor hot, extremely wet forests with dense rich vegetation
204     * @param grasslandColor prairies that are dry and usually wind-swept, but not especially hot or cold
205     * @param woodlandColor part-way between a prairie and a forest; not especially hot or cold
206     * @param seasonalForestColor forest that becomes barren in winter (deciduous trees); not especially hot or cold
207     * @param temperateRainforestColor forest that tends to be slightly warm but very wet
208     * @param borealForestColor forest that tends to be cold and very wet
209     * @param tundraColor very cold plains that still have some low-lying vegetation; also called taiga 
210     * @param iceColor cold barren land covered in permafrost; also used for rivers and lakes that are frozen
211     * @param beachColor sandy or otherwise light-colored shorelines; here, these are more common in warmer places
212     * @param rockyColor rocky or otherwise rugged shorelines; here, these are more common in colder places
213     * @param shallowColor the color of very shallow water; will be mixed with {@code deepColor} to get most ocean colors
214     * @param deepColor the color of very deep water; will be mixed with {@code shallowColor} to get most ocean colors
215     * @param emptyColor the color used for empty space off the edge of the world map; may be transparent
216     */
217    public void initialize(
218            Color desertColor,
219            Color savannaColor,
220            Color tropicalRainforestColor,
221            Color grasslandColor,
222            Color woodlandColor,
223            Color seasonalForestColor,
224            Color temperateRainforestColor,
225            Color borealForestColor,
226            Color tundraColor,
227            Color iceColor,
228            Color beachColor,
229            Color rockyColor,
230            Color shallowColor,
231            Color deepColor,
232            Color emptyColor
233    )
234    {
235        biomeColors[ 0] = desertColor == null ? WorldMapView.desertColor : desertColor.toFloatBits();
236        biomeColors[ 1] = savannaColor == null ? WorldMapView.savannaColor : savannaColor.toFloatBits();
237        biomeColors[ 2] = tropicalRainforestColor == null ? WorldMapView.tropicalRainforestColor : tropicalRainforestColor.toFloatBits();
238        biomeColors[ 3] = grasslandColor == null ? WorldMapView.grasslandColor : grasslandColor.toFloatBits();
239        biomeColors[ 4] = woodlandColor == null ? WorldMapView.woodlandColor : woodlandColor.toFloatBits();
240        biomeColors[ 5] = seasonalForestColor == null ? WorldMapView.seasonalForestColor : seasonalForestColor.toFloatBits();
241        biomeColors[ 6] = temperateRainforestColor == null ? WorldMapView.temperateRainforestColor : temperateRainforestColor.toFloatBits();
242        biomeColors[ 7] = borealForestColor == null ? WorldMapView.borealForestColor : borealForestColor.toFloatBits();
243        biomeColors[ 8] = tundraColor == null ? WorldMapView.tundraColor : tundraColor.toFloatBits();
244        biomeColors[ 9] = iceColor == null ? WorldMapView.iceColor : iceColor.toFloatBits();
245        biomeColors[10] = beachColor == null ? WorldMapView.beachColor : beachColor.toFloatBits();
246        biomeColors[11] = rockyColor == null ? WorldMapView.rockyColor : rockyColor.toFloatBits();
247        biomeColors[12] = shallowColor == null ? WorldMapView.shallowColor : shallowColor.toFloatBits();
248        biomeColors[13] = deepColor == null ? WorldMapView.deepColor : deepColor.toFloatBits();
249        biomeColors[14] = emptyColor == null ? WorldMapView.emptyColor : emptyColor.toFloatBits();
250        float b, diff;
251        for (int i = 0; i < 60; i++) {
252            b = BIOME_TABLE[i];
253            diff = (b % 1.0f - 0.48f) * 0.27f;
254            BIOME_COLOR_TABLE[i] = b = diff >= 0
255                    ? SColor.lightenFloat(biomeColors[(int)b], diff)
256                    : SColor.darkenFloat(biomeColors[(int)b], -diff);
257            BIOME_DARK_COLOR_TABLE[i] = SColor.darkenFloat(b, 0.08f);
258        }
259        BIOME_COLOR_TABLE[60] = BIOME_DARK_COLOR_TABLE[60] = biomeColors[14];
260        biomeColors[ 0] = WorldMapView.desertColor;
261        biomeColors[ 1] = WorldMapView.savannaColor;
262        biomeColors[ 2] = WorldMapView.tropicalRainforestColor;
263        biomeColors[ 3] = WorldMapView.grasslandColor;
264        biomeColors[ 4] = WorldMapView.woodlandColor;
265        biomeColors[ 5] = WorldMapView.seasonalForestColor;
266        biomeColors[ 6] = WorldMapView.temperateRainforestColor;
267        biomeColors[ 7] = WorldMapView.borealForestColor;
268        biomeColors[ 8] = WorldMapView.tundraColor;
269        biomeColors[ 9] = WorldMapView.iceColor;
270        biomeColors[10] = WorldMapView.beachColor;
271        biomeColors[11] = WorldMapView.rockyColor;
272        biomeColors[12] = WorldMapView.shallowColor;
273        biomeColors[13] = WorldMapView.deepColor;
274        biomeColors[14] = WorldMapView.emptyColor;
275    }
276
277
278    public void generate()
279    {
280        wildMap.generate();
281        float baseColor = BIOME_COLOR_TABLE[wildMap.biome & 1023];
282        int h = 1234567890, change, seed = wildMap.rng.nextInt();
283        ICellVisible icv;
284        for (int x = 0; x < width; x++) {
285            for (int y = 0; y < height; y++) {
286                change = h += IntPointHash.hashAll(x, y, 
287                        seed);
288                if((icv = viewer.get(wildMap.floorTypes.get(wildMap.floors[x][y]))) != null) 
289                    colorMap[x][y] = SColor.toEditedFloat(icv.getPackedColor(),
290                        0x1p-12f * ((h & 0xFF) - 0x9F + (change >>> 8 & 0x3F)),
291                        0x1.8p-12f * ((h >>> 8 & 0xFF) - 0xB0 + (change >>> 16 & 0x3F)) - 0.0625f,
292                        0x1.3p-12f * ((h >>> 16 & 0xFF) - 0x90 + (change >>> 24 & 0x3F)),
293                        0f);
294                else 
295                    colorMap[x][y] = SColor.toEditedFloat(baseColor,
296                        0x1p-12f * ((h & 0xFF) - 0x9F + (change >>> 8 & 0x3F)),
297                        0x1.8p-12f * ((h >>> 8 & 0xFF) - 0xB0 + (change >>> 16 & 0x3F)) - 0.0625f,
298                        0x1.3p-12f * ((h >>> 16 & 0xFF) - 0x90 + (change >>> 24 & 0x3F)),
299                        0f);
300
301            }
302        }
303    }
304    
305    public void show(SparseLayers layers)
306    {
307        ArrayTools.insert(colorMap, layers.backgrounds, 0, 0);
308        int c;
309        ICellVisible icv;
310        for (int x = 0; x < width && x < layers.gridWidth; x++) {
311            for (int y = 0; y < height && y < layers.gridHeight; y++) {
312                if((c = wildMap.content[x][y]) >= 0 && (icv = viewer.get(wildMap.contentTypes.get(c))) != null)
313                    layers.put(x, y, icv.getSymbol(), SColor.contrastLuma(icv.getPackedColor(), colorMap[x][y]));
314                else if((icv = viewer.get(wildMap.floorTypes.get(wildMap.floors[x][y]))) != null)
315                    layers.put(x, y, icv.getSymbol(), SColor.contrastLuma(icv.getPackedColor(), colorMap[x][y]));
316            }
317        }
318    }
319}