001package squidpony.squidgrid.gui.gdx; 002 003import com.badlogic.gdx.Gdx; 004import com.badlogic.gdx.graphics.Camera; 005import com.badlogic.gdx.graphics.Color; 006import com.badlogic.gdx.graphics.g2d.Batch; 007import com.badlogic.gdx.math.Frustum; 008import com.badlogic.gdx.math.MathUtils; 009import com.badlogic.gdx.scenes.scene2d.Action; 010import com.badlogic.gdx.scenes.scene2d.Stage; 011import com.badlogic.gdx.scenes.scene2d.actions.Actions; 012import com.badlogic.gdx.scenes.scene2d.actions.TemporalAction; 013import com.badlogic.gdx.utils.IntIntMap; 014import com.badlogic.gdx.utils.viewport.Viewport; 015import squidpony.IColorCenter; 016 017import java.util.ArrayList; 018 019/** 020 * A subclass of SparseLayers that acts almost the same, but uses 3x3 subcells of background color for every cell that 021 * may contain a char. Methods that affect the background sometimes specify positions in subcells, which allows 022 * affecting less than one cell with some visual effect. You will usually want to use a triple-size resistance and FOV 023 * map; you can get a resistance map that respects subcells with 024 * {@link squidpony.squidgrid.mapping.DungeonUtility#generateResistances3x3(char[][])} (this looks better if it is given 025 * a "line" dungeon as produced by {@link squidpony.squidgrid.mapping.DungeonUtility#hashesToLines(char[][], boolean)}), 026 * and then you can use that resistance map with normal FOV methods, just typically at triple vision range. Much of the 027 * time, it makes sense to run both that triple-range FOV, and a normal-range FOV with a resistance map calculated in 028 * cells; you can use the normal-range FOV for gameplay and AI calculations and the triple-range for visual display. 029 * <br> 030 * Created by Tommy Ettinger on 8/28/2018. 031 */ 032public class SubcellLayers extends SparseLayers { 033 public SubcellLayers(int gridWidth, int gridHeight) 034 { 035 this(gridWidth, gridHeight, 10, 16, DefaultResources.getStretchableFont()); 036 } 037 038 public SubcellLayers(int gridWidth, int gridHeight, float cellWidth, float cellHeight) 039 { 040 this(gridWidth, gridHeight, cellWidth, cellHeight, DefaultResources.getStretchableFont()); 041 } 042 043 public SubcellLayers(int gridWidth, int gridHeight, float cellWidth, float cellHeight, TextCellFactory font) { 044 this(gridWidth, gridHeight, cellWidth, cellHeight, font, 0f, 0f); 045 } 046 047 public SubcellLayers(int gridWidth, int gridHeight, float cellWidth, float cellHeight, TextCellFactory font, float xOffset, float yOffset) { 048 this.gridWidth = MathUtils.clamp(gridWidth, 1, 65535); 049 this.gridHeight = MathUtils.clamp(gridHeight, 1, 65535); 050 backgrounds = new float[this.gridWidth*3][this.gridHeight*3]; 051 layers = new ArrayList<>(4); 052 if(font.initialized()) 053 this.font = font; 054 else 055 this.font = font.width(cellWidth).height(cellHeight).initBySize(); 056 layers.add(new SparseTextMap(gridWidth * gridHeight >> 2)); 057 mapping = new IntIntMap(4); 058 mapping.put(0, 0); 059 glyphs = new ArrayList<>(16); 060 scc = DefaultResources.getSCC(); 061 setBounds(xOffset, yOffset, 062 this.font.actualCellWidth * this.gridWidth, this.font.actualCellHeight * this.gridHeight); 063 } 064 065 /** 066 * Places the given char 2D array, if-non-null, in the default foreground color starting at x=0, y=0, while also 067 * setting the background colors to match the given Color 2D array. The colors 2D array should have a width that is 068 * 3 * {@link #gridWidth} and a height that is 3 * {@link #gridHeight}. If the colors argument is null, does not 069 * affect backgrounds but may still affect chars. If the chars argument is null, only affects the background colors. 070 * This will filter each Color in colors if the color center this uses has a filter. 071 * 072 * @param chars Can be {@code null}, indicating that only colors must be put. 073 * @param colors the background colors for the given chars; this array should have 3 times the width and height of chars 074 */ 075 @Override 076 public void put(char[][] chars, Color[][] colors) { 077 super.putChars(chars); 078 super.put(null, colors); 079 } 080 081 /** 082 * Places the given char 2D array, if-non-null, in the default foreground color starting at x=0, y=0, while also 083 * setting the background colors to match the given 2D array of colors as packed floats. The colors 2D array should 084 * have a width that is 3 * {@link #gridWidth} and a height that is 3 * {@link #gridHeight}. If the colors argument 085 * is null, does not affect backgrounds but may still affect chars. If the chars argument is null, only affects the 086 * background colors. This will not filter the passed colors at all. 087 * 088 * @param chars Can be {@code null}, indicating that only colors must be put. 089 * @param colors the background colors for the given chars; this array should have 3 times the width and height of chars 090 */ 091 @Override 092 public void put(char[][] chars, float[][] colors) { 093 super.putChars(chars); 094 super.put(null, colors); 095 } 096 097 /** 098 * Places the given char 2D array, if-non-null, with the given foreground colors in the first Color 2D array, 099 * starting at x=0, y=0, while also setting the background colors to match the second Color 2D array. If the 100 * bgColors argument is null, only affects foreground chars and colors. If the chars argument or the fgColors 101 * argument is null, only affects the background colors. Any positions where a Color in fgColors is null will not 102 * have a char placed (this can be used to restrict what is placed). This will filter each Color in the background 103 * and foreground if the color center this uses has a filter. 104 * 105 * @param chars Can be {@code null}, indicating that only colors must be put. 106 * @param fgColors the foreground Colors for the given chars 107 * @param bgColors the background Colors for the given chars; this array should have 3 times the width and height of chars 108 */ 109 @Override 110 public void put(char[][] chars, Color[][] fgColors, Color[][] bgColors) { 111 super.putChars(chars, fgColors); 112 super.put(null, bgColors); 113 } 114 115 /** 116 * Places the given char 2D array, if-non-null, with the given foreground colors in the first float 2D array, 117 * starting at x=0, y=0, while also setting the background colors to match the second float 2D array. If the 118 * bgColors argument is null, only affects foreground chars and colors. If the chars argument or the fgColors 119 * argument is null, only affects the background colors. Any positions where a float in fgColors is 0 will not 120 * have a char placed (this can be used to restrict what is placed). This will not filter any colors. 121 * 122 * @param chars Can be {@code null}, indicating that only colors must be put. 123 * @param fgColors the foreground colors for the given chars, as packed floats 124 * @param bgColors the background colors for the given chars, as packed floats; this array should have 3 times the width and height of chars 125 */ 126 @Override 127 public void put(char[][] chars, float[][] fgColors, float[][] bgColors) { 128 super.putChars(chars, fgColors); 129 super.put(null, bgColors); 130 } 131 132 /** 133 * Puts the char c at the position x,y with the given foreground and background colors. If foreground is null or is 134 * fully transparent (all channels 0), then this does not change the foreground contents. 135 * If background is null or is fully transparent, this does not change the background. Uses the color center to 136 * potentially filter the given colors; this can be changed with {@link #setScc(IColorCenter)}. 137 * 138 * @param x the x position to place the char at 139 * @param y the y position to place the char at 140 * @param c the char to place 141 * @param foreground the color to use for c; if null or fully transparent, nothing will change in the foreground 142 * @param background the color to use for the cell; if null, nothing will change in the background 143 */ 144 @Override 145 public void put(int x, int y, char c, Color foreground, Color background) { 146 super.put(x, y, c, foreground, null); 147 if(background != null) 148 put(x, y, background); 149 } 150 151 /** 152 * Puts the char c at the position x,y with the given foreground and background colors as encoded floats, such as 153 * those produced by {@link Color#toFloatBits()}. If foreground is 0f, then this does not 154 * change the foreground contents. If background is 0f, this does not change the background. Does not filter the 155 * given colors. 156 * 157 * @param x the x position to place the char at 158 * @param y the y position to place the char at 159 * @param c the char to place 160 * @param foreground the color to use for c; if 0f, nothing will change in the foreground 161 * @param background the color to use for the cell; if null or fully transparent, nothing will change in the background 162 */ 163 @Override 164 public void put(int x, int y, char c, float foreground, float background) { 165 super.put(x, y, c, foreground, 0f); 166 if(background != 0f) 167 put(x, y, background); 168 } 169 170 /** 171 * Puts the char c at the position x,y in the requested layer with the given foreground and background colors. If 172 * foreground is null or is fully transparent (all channels 0), then this does not change 173 * the foreground contents. If background is null or is fully transparent, this does not change the background. Uses 174 * the color center to potentially filter the given colors; this can be changed with 175 * {@link #setColorCenter(IColorCenter)}. The layer can be greater than the number of layers currently present, 176 * which will add a layer to be rendered over the existing layers, but the number that refers to that layer will not 177 * change. It is recommended that to add a layer, you only add at the value equal to {@link #getLayerCount()}, which 178 * will maintain the order and layer numbers in a sane way. 179 * 180 * @param x the x position to place the char at 181 * @param y the y position to place the char at 182 * @param c the char to place 183 * @param foreground the color to use for c; if null or fully transparent, nothing will change in the foreground 184 * @param background the color to use for the cell; if null or fully transparent, nothing will change in the background 185 * @param layer the layer to place the colorful char into; should usually be between 0 and {@link #getLayerCount()} inclusive 186 */ 187 @Override 188 public void put(int x, int y, char c, Color foreground, Color background, int layer) { 189 super.put(x, y, c, foreground, null, layer); 190 if(background != null) 191 put(x, y, background); 192 } 193 194 /** 195 * Puts the char c at the position x,y in the requested layer with the given foreground and background colors as 196 * encoded floats, such as those produced by {@link Color#toFloatBits()}. If foreground is 0f, then this does not 197 * change the foreground contents. If background is 0f, this does not change the 198 * background. Does not filter the given colors. The layer can be greater than the number of layers currently 199 * present, which will add a layer to be rendered over the existing layers, but the number that refers to that layer 200 * will not change. It is recommended that to add a layer, you only add at the value equal to 201 * {@link #getLayerCount()}, which will maintain the order and layer numbers in a sane way. 202 * 203 * @param x the x position to place the char at 204 * @param y the y position to place the char at 205 * @param c the char to place 206 * @param foreground the color to use for c; if 0f, nothing will change in the foreground 207 * @param background the color to use for the cell; if null or fully transparent, nothing will change in the background 208 * @param layer the layer to place the colorful char into; should usually be between 0 and {@link #getLayerCount()} inclusive 209 */ 210 @Override 211 public void put(int x, int y, char c, float foreground, float background, int layer) { 212 super.put(x, y, c, foreground, 0f, layer); 213 if(background != 0f) 214 put(x, y, background); 215 216 } 217 218 /** 219 * Puts text at the position x,y with the given foreground and background colors. If foreground is null or is 220 * fully transparent (all channels 0), then this does not change the foreground contents. 221 * If background is null or is fully transparent, this does not change the background. Uses the color center to 222 * potentially filter the given colors; this can be changed with {@link #setColorCenter(IColorCenter)}. 223 * 224 * @param x the x position to place the String at 225 * @param y the y position to place the String at 226 * @param text the String to place 227 * @param foreground the color to use for text; if null or fully transparent, nothing will change in the foreground 228 * @param background the color to use for the cells; if null or fully transparent, nothing will change in the background 229 */ 230 @Override 231 public void put(int x, int y, String text, Color foreground, Color background) { 232 super.put(x, y, text, foreground, null); 233 if(background != null && text != null) 234 { 235 for (int i = 0; i < text.length(); i++) { 236 put(x+i, y, background); 237 } 238 } 239 } 240 241 /** 242 * Puts text at the position x,y with the given foreground and background colors as encoded floats, such as 243 * those produced by {@link Color#toFloatBits()}. If foreground is 0f, then this does not change the foreground 244 * contents. If background is 0f, this does not change the background. Does not filter the given colors. 245 * 246 * @param x the x position to place the String at 247 * @param y the y position to place the String at 248 * @param text the String to place 249 * @param foreground the color to use for text; if 0f, nothing will change in the foreground 250 * @param background the color to use for the cells; if null or fully transparent, nothing will change in the background 251 */ 252 @Override 253 public void put(int x, int y, String text, float foreground, float background) { 254 super.put(x, y, text, foreground, 0f); 255 if(background != 0f && text != null) 256 { 257 for (int i = 0; i < text.length(); i++) { 258 put(x+i, y, background); 259 } 260 } 261 } 262 263 /** 264 * Puts text at the position x,y in the requested layer with the given foreground and background colors. If 265 * foreground is null or is fully transparent (all channels 0), then this does not change the foreground contents. 266 * If background is null or is fully transparent, this does not change the background. Uses the color center to 267 * potentially filter the given colors; this can be changed with {@link #setColorCenter(IColorCenter)}. The layer 268 * can be greater than the number of layers currently present, which will add a layer to be rendered over the 269 * existing layers, but the number that refers to that layer will not change. It is recommended that to add a layer, 270 * you only add at the value equal to {@link #getLayerCount()}, which will maintain the order and layer numbers in a 271 * sane way. 272 * 273 * @param x the x position to place the String at 274 * @param y the y position to place the String at 275 * @param text the String to place 276 * @param foreground the color to use for text; if null or fully transparent, nothing will change in the foreground 277 * @param background the color to use for the cells; if null or fully transparent, nothing will change in the background 278 * @param layer the layer to place the colorful char into; should usually be between 0 and {@link #getLayerCount()} inclusive 279 */ 280 @Override 281 public void put(int x, int y, String text, Color foreground, Color background, int layer) { 282 super.put(x, y, text, foreground, null, layer); 283 if(background != null && text != null) 284 { 285 for (int i = 0; i < text.length(); i++) { 286 put(x+i, y, background); 287 } 288 } 289 } 290 291 /** 292 * Puts text at the position x,y in the requested layer with the given foreground and background colors as encoded 293 * floats, such as those produced by {@link Color#toFloatBits()}. If foreground is 0f, then this does not change the 294 * foreground contents. If background is 0f, this does not change the background. Does not filter the given colors. 295 * The layer can be greater than the number of layers currently present, which will add a layer to be rendered over 296 * the existing layers, but the number that refers to that layer will not change. It is recommended that to add a 297 * layer, you only add at the value equal to {@link #getLayerCount()}, which will maintain the order and layer 298 * numbers in a sane way. 299 * 300 * @param x the x position to place the String at 301 * @param y the y position to place the String at 302 * @param text the String to place 303 * @param foreground the color to use for text; if 0f, nothing will change in the foreground 304 * @param background the color to use for the cells; if null or fully transparent, nothing will change in the background 305 * @param layer the layer to place the colorful char into; should usually be between 0 and {@link #getLayerCount()} inclusive 306 */ 307 @Override 308 public void put(int x, int y, String text, float foreground, float background, int layer) { 309 super.put(x, y, text, foreground, 0f, 0); 310 if(background != 0f && text != null) 311 { 312 for (int i = 0; i < text.length(); i++) { 313 put(x+i, y, background); 314 } 315 } 316 } 317 318 /** 319 * Changes the background at position x,y to the given Color. If the color is null, then this will make the 320 * background fully transparent at the specified position. 321 * 322 * @param x where to change the background color, x-coordinate 323 * @param y where to change the background color, y-coordinate 324 * @param color the Color to change to; if null will be considered fully transparent 325 */ 326 @Override 327 public void put(int x, int y, Color color) { 328 final float bg = (color == null) ? 0x0.0p0F : scc.filter(color).toFloatBits(); 329 x *= 3; 330 y *= 3; 331 backgrounds[x ][y ] = bg; 332 backgrounds[x ][y+1] = bg; 333 backgrounds[x ][y+2] = bg; 334 backgrounds[x+1][y ] = bg; 335 backgrounds[x+1][y+1] = bg; 336 backgrounds[x+1][y+2] = bg; 337 backgrounds[x+2][y ] = bg; 338 backgrounds[x+2][y+1] = bg; 339 backgrounds[x+2][y+2] = bg; 340 } 341 342 /** 343 * Changes the background at position x,y to the given color as an encoded float. The color can be transparent, 344 * which will show through to whatever is behind this SparseLayers, or the color the screen was last cleared with. 345 * Unlike other methods in this class, a float equal to 0f will be used instead of being used to skip over a cell, 346 * and will change the background at the given position to fully transparent. 347 * 348 * @param x where to change the background color, x-coordinate 349 * @param y where to change the background color, y-coordinate 350 * @param bg the color, as an encoded float, to change to; may be transparent, and considers 0f a valid color 351 */ 352 @Override 353 public void put(int x, int y, float bg) { 354 x *= 3; 355 y *= 3; 356 backgrounds[x ][y ] = bg; 357 backgrounds[x ][y+1] = bg; 358 backgrounds[x ][y+2] = bg; 359 backgrounds[x+1][y ] = bg; 360 backgrounds[x+1][y+1] = bg; 361 backgrounds[x+1][y+2] = bg; 362 backgrounds[x+2][y ] = bg; 363 backgrounds[x+2][y+1] = bg; 364 backgrounds[x+2][y+2] = bg; 365 } 366 367 /** 368 * A convenience method that handles blending the background color with a specified light color, by a specific 369 * amount, without putting a char on the screen; as a whole this affects one x,y position. 370 * 371 * @param x the x position, in cells 372 * @param y the y position, in cells 373 * @param background the "base" color to use for the background, which will be combined with lightColor, as a libGDX Color 374 * @param lightColor the color to mix with the background, as a libGDX Color 375 * @param lightAmount a float that determines how much lightColor should affect background by; not strictly limited 376 */ 377 @Override 378 public void putWithLight(int x, int y, Color background, Color lightColor, float lightAmount) { 379 putWithLight(x, y, background == null ? 0f : background.toFloatBits(), 380 lightColor == null ? 0f : lightColor.toFloatBits(), lightAmount); 381 } 382 383 /** 384 * A convenience method that handles blending the background color with a specified light color, by a specific 385 * amount, without putting a char on the screen; as a whole this affects one x,y position. 386 * 387 * @param x the x position, in subcells (3 subcells in a row have the width of one character cell) 388 * @param y the y position, in subcells (3 subcells in a column have the height of one character cell) 389 * @param background the "base" color to use for the background, which will be combined with lightColor, as a packed float 390 * @param lightColor the color to mix with the background, as a packed float 391 * @param lightAmount a float that determines how much lightColor should affect background by; not strictly limited 392 */ 393 @Override 394 public void putWithLight(int x, int y, float background, float lightColor, float lightAmount) { 395 putSingle(x, y, 396 SColor.lerpFloatColors(background, lightColor, 397 MathUtils.clamp(0xAAp-9f + (0xC8p-9f * lightAmount), 0f, 1f)) 398 ); 399 400 } 401 402 /** 403 * A convenience method that handles blending the background color with a specified light color, by a specific 404 * amount, without putting a char on the screen; as a whole this affects one x,y position. This will use the same 405 * brightness for all cells given identical lightAmount values when this is called; this differentiates it from 406 * the putWithLight methods that take a Noise3D argument, which would light "splotches" of map with 407 * brighter or darker color. Instead, if lightAmount is obtained via SquidLib's {@code FOV} class, then all cells 408 * at a short distance from an FOV center will be lit brightly and cells far away will flicker in and out of view. 409 * 410 * @param x the x position, in cells 411 * @param y the y position, in cells 412 * @param background the "base" color to use for the background, which will be combined with lightColor, as a packed float color 413 * @param lightColor the color to mix with the background, as a packed float color 414 * @param lightAmount a float that determines how much lightColor should affect background by; not strictly limited 415 * to between 0 and 1, and negative values can be given to favor background more 416 * @param flickerSpeed a small float multiplier applied to the time in milliseconds; often between 0.0005f and 0.005f 417 */ 418 @Override 419 public void putWithConsistentLight(int x, int y, float background, float lightColor, float lightAmount, float flickerSpeed) { 420 super.putWithConsistentLight(x, y, background, lightColor, lightAmount, flickerSpeed); 421 } 422 423 /** 424 * A convenience method that handles blending the background color with a specified light color, by a specific 425 * amount, without putting a char on the screen; as a whole this affects one x,y position. This will use the same 426 * brightness for all cells given identical lightAmount values when this is called; this differentiates it from 427 * the putWithLight methods that take a Noise3D argument, which would light "splotches" of map with 428 * brighter or darker color. Instead, if lightAmount is obtained via SquidLib's {@code FOV} class, then all cells 429 * at a short distance from an FOV center will be lit brightly and cells far away will flicker in and out of view. 430 * 431 * @param x the x position, in cells 432 * @param y the y position, in cells 433 * @param background the "base" color to use for the background, which will be combined with lightColor, as a libGDX Color 434 * @param lightColor the color to mix with the background, as a libGDX Color 435 * @param lightAmount a float that determines how much lightColor should affect background by; not strictly limited 436 * to between 0 and 1, and negative values can be given to favor background more 437 * @param flickerSpeed a small float multiplier applied to the time in milliseconds; often between 0.0005f and 0.005f 438 */ 439 @Override 440 public void putWithConsistentLight(int x, int y, Color background, Color lightColor, float lightAmount, float flickerSpeed) { 441 super.putWithConsistentLight(x, y, background, lightColor, lightAmount, flickerSpeed); 442 } 443 444 /** 445 * Sets a single subcell of the background to use the specified color as a packed float. The values given for x and 446 * y refer to a triple-width, triple-height grid, so the meaning of x and y are different here from other methods. 447 * If you want to set the center subcell of the cell that would normally be set with 448 * {@link #put(int, int, char, float, float)} at the position x=20,y=30, this would set that subcell at 449 * x=20*3+1,y=30*3+1 , where the *3 gets the x and y to refer to the correct 3x3 subcell grid, and the +1 chooses 450 * the subcell as one to the right and one down. 451 * @param x the x position in the triple-width, triple-height grid of background subcells 452 * @param y the y position in the triple-width, triple-height grid of background subcells 453 * @param color a packed float color to place in the background of one subcell 454 */ 455 public void putSingle(int x, int y, float color) 456 { 457 backgrounds[x][y] = color; 458 } 459 460 /** 461 * Sets the background colors to match the given Color 2D array. The colors 2D array should have a width that is 3 * 462 * {@link #gridWidth} and a height that is 3 * {@link #gridHeight}. If the colors argument is null, does nothing. 463 * This will filter each Color in colors if the color center this uses has a filter. 464 * 465 * @param colors the background colors for the given chars 466 */ 467 @Override 468 public void put(Color[][] colors) { 469 super.put(colors); 470 } 471 472 /** 473 * Sets the background colors to match the given 2D array of colors as packed floats. The colors 2D array should 474 * have a width that is 3 * {@link #gridWidth} and a height that is 3 * {@link #gridHeight}. If the colors argument 475 * is null, does nothing. This will not filter the passed colors at all. 476 * 477 * @param colors the background colors to use for this SparseLayers 478 */ 479 @Override 480 public void put(float[][] colors) { 481 super.put(colors); 482 } 483 484 /** 485 * Removes the foreground chars, where present, in all layers at the given x,y position. 486 * The backgrounds will be unchanged. 487 * 488 * @param x the x-coordinate of the position to remove all chars from 489 * @param y the y-coordinate of the position to remove all chars from 490 */ 491 @Override 492 public void clear(int x, int y) { 493 if(x < 0 || x >= gridWidth || y < 0 || y >= gridHeight) 494 return; 495 int code = SparseTextMap.encodePosition(x, y); 496 for (int i = 0; i < layers.size(); i++) { 497 layers.get(i).remove(code); 498 } 499 x *= 3; 500 y *= 3; 501 backgrounds[x ][y ] = 0f; 502 backgrounds[x ][y+1] = 0f; 503 backgrounds[x ][y+2] = 0f; 504 backgrounds[x+1][y ] = 0f; 505 backgrounds[x+1][y+1] = 0f; 506 backgrounds[x+1][y+2] = 0f; 507 backgrounds[x+2][y ] = 0f; 508 backgrounds[x+2][y+1] = 0f; 509 backgrounds[x+2][y+2] = 0f; 510 511 } 512 513 /** 514 * Changes the background color in an area to all have the given color, as a libGDX Color (or SColor, etc.). 515 * This uses subcell measurements for x, y, width, and height, where the grid width in subcells is 516 * 3 * {@link #gridWidth} and the grid height in subcells is 3 * {@link #gridHeight}. To affect a full cell, this 517 * needs a width and height of at least 3 each; anything less will affect only some subcells. 518 * 519 * @param color a libGDX Color to fill the area with; may be null to make the background transparent 520 * @param x left edge's x coordinate, in subcells (3 subcells to one cell horizontally) 521 * @param y top edge's y coordinate, in subcells (3 subcells to one cell vertically) 522 * @param width the width of the area to change the color on, in subcells (3 subcells to one cell horizontally) 523 * @param height the height of the area to change the color on, in subcells (3 subcells to one cell vertically) 524 */ 525 @Override 526 public void fillArea(Color color, int x, int y, int width, int height) { 527 fillArea(color == null ? 0f : scc.filter(color).toFloatBits(), x, y, width, height); 528 } 529 /** 530 * Changes the background color in an area to all have the given color, as a packed float. 531 * This uses subcell measurements for x, y, width, and height, where the grid width in subcells is 532 * 3 * {@link #gridWidth} and the grid height in subcells is 3 * {@link #gridHeight}. To affect a full cell, this 533 * needs a width and height of at least 3 each; anything less will affect only some subcells. 534 * 535 * @param color a color as a packed float to fill the area with; may be 0f to make the background transparent 536 * @param x left edge's x coordinate, in subcells (3 subcells to one cell horizontally) 537 * @param y top edge's y coordinate, in subcells (3 subcells to one cell vertically) 538 * @param width the width of the area to change the color on, in subcells (3 subcells to one cell horizontally) 539 * @param height the height of the area to change the color on, in subcells (3 subcells to one cell vertically) 540 */ 541 public void fillArea(float color, int x, int y, int width, int height) { 542 if (x < 0) { 543 width += x; 544 x = 0; 545 } 546 if (y < 0) { 547 height += y; 548 y = 0; 549 } 550 if (width <= 0 || height <= 0) 551 return; 552 final int gw = gridWidth * 3, gh = gridHeight * 3; 553 for (int i = 0, xx = x; i < width && xx < gw; i++, xx++) { 554 for (int j = 0, yy = y; j < height && yy < gh; j++, yy++) { 555 backgrounds[xx][yy] = color; 556 } 557 } 558 } 559 560 /** 561 * Using the existing background color at the subcell position x,y, this performs color blending from that existing 562 * color to the given color (as a float), using the mixBy parameter to determine how much of the color parameter to 563 * use (1f will set the color in this to the parameter, while 0f for mixBy will ignore the color parameter 564 * entirely). The x and y parameters are in subcells, where 3 subcells have the same width or height as one cell (a 565 * cell holds a char, and 3x3 subcells fit in the same area). 566 * @param x the x component of the position in this panel to draw the starting color from 567 * @param y the y component of the position in this panel to draw the starting color from 568 * @param color the new color to mix with the starting color; a packed float, as made by {@link Color#toFloatBits()} 569 * @param mixBy the amount by which the new color will affect the old one, between 0 (no effect) and 1 (overwrite) 570 */ 571 @Override 572 public void blend(int x, int y, float color, float mixBy) 573 { 574 backgrounds[x][y] = SColor.lerpFloatColorsBlended(backgrounds[x][y], color, mixBy); 575 } 576 577 /** 578 * Tints the background at position x,y (in cells) so it becomes the given encodedColor, waiting for {@code delay} 579 * (in seconds) before performing it, then after the tint is complete it returns the cell to its original color, 580 * taking duration seconds. Additionally, enqueue {@code postRunnable} for running after the created action ends. 581 * All subcells in the tinted cell will reach the same color during this animation, but the subcells can start with 582 * different colors, and they will return to those starting colors after this animation finishes. 583 * <br> 584 * This will only behave correctly if you call {@link Stage#act()} before you call {@link Stage#draw()}, but after 585 * any changes to the contents of this SparseLayers. If you change the contents, then draw, and then act, that will 586 * draw the contents without the tint this applies, then apply the tint when you call act(), then quickly overwrite 587 * the tint in the next frame. That visually appears as nothing happening other than a delay. 588 * @param delay how long to wait in seconds before starting the effect 589 * @param x the x-coordinate of the cell to tint 590 * @param y the y-coordinate of the cell to tint 591 * @param encodedColor what to transition the cell's color towards, and then transition back from, as a packed float 592 * @param duration how long the total "round-trip" transition should take in seconds 593 * @param postRunnable a Runnable to execute after the tint completes; may be null to do nothing. 594 */ 595 public void tint(final float delay, final int x, final int y, final float encodedColor, float duration, 596 /* @Nullable */ Runnable postRunnable) { 597 if(x < 0 || x >= gridWidth || y < 0 || y >= gridHeight) 598 return; 599 duration = Math.max(0.015f, duration); 600 final int xx = x * 3, yy = y * 3; 601 final float 602 x0y0 = backgrounds[xx][yy], x1y0 = backgrounds[xx+1][yy], x2y0 = backgrounds[xx+2][yy], 603 x0y1 = backgrounds[xx][yy+1], x1y1 = backgrounds[xx+1][yy+1], x2y1 = backgrounds[xx+2][yy+1], 604 x0y2 = backgrounds[xx][yy+2], x1y2 = backgrounds[xx+1][yy+2], x2y2 = backgrounds[xx+2][yy+2]; 605 final int nbActions = 3 + (0 < delay ? 1 : 0) + (postRunnable == null ? 0 : 1); 606 final Action[] sequence = new Action[nbActions]; 607 int index = 0; 608 if (0 < delay) 609 sequence[index++] = Actions.delay(delay); 610 sequence[index++] = new TemporalAction(duration * 0.3f) { 611 @Override 612 protected void update(float percent) { 613 backgrounds[xx ][yy ] = SColor.lerpFloatColors(x0y0, encodedColor, percent); 614 backgrounds[xx ][yy+1] = SColor.lerpFloatColors(x0y1, encodedColor, percent); 615 backgrounds[xx ][yy+2] = SColor.lerpFloatColors(x0y2, encodedColor, percent); 616 backgrounds[xx+1][yy ] = SColor.lerpFloatColors(x1y0, encodedColor, percent); 617 backgrounds[xx+1][yy+1] = SColor.lerpFloatColors(x1y1, encodedColor, percent); 618 backgrounds[xx+1][yy+2] = SColor.lerpFloatColors(x1y2, encodedColor, percent); 619 backgrounds[xx+2][yy ] = SColor.lerpFloatColors(x2y0, encodedColor, percent); 620 backgrounds[xx+2][yy+1] = SColor.lerpFloatColors(x2y1, encodedColor, percent); 621 backgrounds[xx+2][yy+2] = SColor.lerpFloatColors(x2y2, encodedColor, percent); 622 } 623 }; 624 sequence[index++] = new TemporalAction(duration * 0.7f) { 625 @Override 626 protected void update(float percent) { 627 backgrounds[xx ][yy ] = SColor.lerpFloatColors(encodedColor, x0y0, percent); 628 backgrounds[xx ][yy+1] = SColor.lerpFloatColors(encodedColor, x0y1, percent); 629 backgrounds[xx ][yy+2] = SColor.lerpFloatColors(encodedColor, x0y2, percent); 630 backgrounds[xx+1][yy ] = SColor.lerpFloatColors(encodedColor, x1y0, percent); 631 backgrounds[xx+1][yy+1] = SColor.lerpFloatColors(encodedColor, x1y1, percent); 632 backgrounds[xx+1][yy+2] = SColor.lerpFloatColors(encodedColor, x1y2, percent); 633 backgrounds[xx+2][yy ] = SColor.lerpFloatColors(encodedColor, x2y0, percent); 634 backgrounds[xx+2][yy+1] = SColor.lerpFloatColors(encodedColor, x2y1, percent); 635 backgrounds[xx+2][yy+2] = SColor.lerpFloatColors(encodedColor, x2y2, percent); 636 } 637 }; 638 if(postRunnable != null) 639 { 640 sequence[index++] = Actions.run(postRunnable); 641 } 642 sequence[index] = Actions.delay(duration, Actions.run(new Runnable() { 643 @Override 644 public void run() { 645 backgrounds[xx ][yy ] = x0y0; 646 backgrounds[xx ][yy+1] = x0y1; 647 backgrounds[xx ][yy+2] = x0y2; 648 backgrounds[xx+1][yy ] = x1y0; 649 backgrounds[xx+1][yy+1] = x1y1; 650 backgrounds[xx+1][yy+2] = x1y2; 651 backgrounds[xx+2][yy ] = x2y0; 652 backgrounds[xx+2][yy+1] = x2y1; 653 backgrounds[xx+2][yy+2] = x2y2; 654 } 655 })); 656 657 addAction(Actions.sequence(sequence)); 658 } 659 /** 660 * Draws the SubcellLayers and all glyphs it tracks. {@link Batch#begin()} must have already been called on the 661 * batch, and {@link Batch#end()} should be called after this returns and before the rendering code finishes for the 662 * frame. 663 * <br> 664 * This will set the shader of {@code batch} if using a distance field or MSDF font and the shader is currently not 665 * configured for such a font; it does not reset the shader to the default so that multiple Actors can all use the 666 * same shader and so specific extra glyphs or other items can be rendered after calling draw(). If you need to draw 667 * both a distance field font and full-color art, you should set the shader on the Batch to null when you want to 668 * draw full-color art, and end the Batch between drawing this object and the other art. 669 * 670 * @param batch a Batch such as a {@link FilterBatch} that must be between a begin() and end() call; usually done by Stage 671 * @param parentAlpha currently ignored 672 */ 673 @Override 674 public void draw(Batch batch, float parentAlpha) { 675 float xo = getX(), yo = getY(), yOff = yo + 1f + gridHeight * font.actualCellHeight, gxo, gyo; 676 font.draw(batch, backgrounds, xo, yo, 3, 3); 677 int len = layers.size(); 678 Frustum frustum = null; 679 Stage stage = getStage(); 680 if(stage != null) { 681 Viewport viewport = stage.getViewport(); 682 if(viewport != null) 683 { 684 Camera camera = viewport.getCamera(); 685 if(camera != null) 686 { 687 if( 688 camera.frustum != null && 689 (!camera.frustum.boundsInFrustum(xo, yOff - font.actualCellHeight - 1f, 0f, font.actualCellWidth, font.actualCellHeight, 0f) || 690 !camera.frustum.boundsInFrustum(xo + font.actualCellWidth * (gridWidth-1), yo, 0f, font.actualCellWidth, font.actualCellHeight, 0f)) 691 ) 692 frustum = camera.frustum; 693 } 694 } 695 } 696 font.configureShader(batch); 697 if(frustum == null) { 698 for (int i = 0; i < len; i++) { 699 layers.get(i).draw(batch, font, xo, yOff); 700 } 701 } 702 else 703 { 704 for (int i = 0; i < len; i++) { 705 layers.get(i).draw(batch, font, frustum, xo, yOff); 706 } 707 } 708 709 int x, y; 710 for (int i = 0; i < glyphs.size(); i++) { 711 TextCellFactory.Glyph glyph = glyphs.get(i); 712 if(glyph == null) 713 continue; 714 glyph.act(Gdx.graphics.getDeltaTime()); 715 if(!glyph.isVisible() || 716 (x = Math.round((gxo = glyph.getX() - xo) / font.actualCellWidth)) < 0 || x >= gridWidth || 717 (y = Math.round((gyo = glyph.getY() - yo) / -font.actualCellHeight + gridHeight)) < 0 || y >= gridHeight || 718 backgrounds[x * 3 + 1][y * 3 + 1] == 0f || (frustum != null && !frustum.boundsInFrustum(gxo, gyo, 0f, font.actualCellWidth, font.actualCellHeight, 0f))) 719 continue; 720 glyph.draw(batch, 1f); 721 } 722 } 723}