001package squidpony.squidgrid.gui.gdx; 002 003import com.badlogic.gdx.Gdx; 004import com.badlogic.gdx.graphics.Color; 005import com.badlogic.gdx.graphics.g2d.Batch; 006import com.badlogic.gdx.graphics.g2d.TextureRegion; 007import com.badlogic.gdx.math.MathUtils; 008import com.badlogic.gdx.scenes.scene2d.Action; 009import com.badlogic.gdx.scenes.scene2d.Actor; 010import com.badlogic.gdx.scenes.scene2d.Group; 011import com.badlogic.gdx.scenes.scene2d.actions.Actions; 012import com.badlogic.gdx.utils.Align; 013import squidpony.ArrayTools; 014import squidpony.IColorCenter; 015import squidpony.StringKit; 016import squidpony.panel.IColoredString; 017import squidpony.squidgrid.Direction; 018import squidpony.squidmath.Coord; 019import squidpony.squidmath.OrderedSet; 020import squidpony.squidmath.StatefulRNG; 021 022import java.util.Collection; 023 024import static com.badlogic.gdx.math.MathUtils.clamp; 025 026/** 027 * Displays text and images in a grid pattern. Supports basic animations. 028 * 029 * Grid width and height settings are in terms of number of cells. Cell width and height 030 * are in terms of number of pixels. 031 * 032 * When text is placed, the background color is set separately from the foreground character. When moved, only the 033 * foreground character is moved. 034 * 035 * @author Eben Howard - http://squidpony.com - howard@squidpony.com 036 */ 037public class SquidPanel extends Group implements IPackedColorPanel { 038 039 public float DEFAULT_ANIMATION_DURATION = 0.12F; 040 protected int animationCount; 041 protected Color defaultForeground = Color.WHITE; 042 protected IColorCenter<Color> scc; 043 protected int cellWidth, cellHeight; 044 protected int gridWidth, gridHeight, gridOffsetX, gridOffsetY; 045 /** 046 * The 2D array of chars that this will render, using x,y indexing. 047 * Full-block cells that are completely filled with their color will be the char at Unicode codepoint 0, 048 * usually represented with {@code '\0'}. 049 */ 050 public char[][] contents; 051 /** 052 * The 2D array of floats representing colors in a way that libGDX can efficiently use, ABGR-packed. 053 * Most use won't directly involve this field, but there are various techniques SquidLib uses to boost 054 * performance by treating a color as a float. This is mainly advantageous when many colors are involved 055 * and objects shouldn't be instantiated many times. You may want to consider using 056 * {@link SColor#lerpFloatColors(float, float, float)} if you expect to smoothly mix these float colors, 057 * which avoids creating intermediate Color objects. There are more methods like that in SColor. 058 */ 059 public float[][] colors; 060 protected Color lightingColor = SColor.WHITE, tmpColor = new Color(); 061 protected TextCellFactory textFactory; 062 protected float xOffset, yOffset, lightingFloat = SColor.FLOAT_WHITE; 063 public OrderedSet<AnimatedEntity> animatedEntities; 064 public OrderedSet<Actor> autoActors; 065 /** 066 * For thin-wall maps, where only cells where x and y are both even numbers have backgrounds displayed. 067 * Should be false when using this SquidPanel for anything that isn't specifically a background of a map 068 * that uses the thin-wall method from ThinDungeonGenerator or something similar. Even the foregrounds of 069 * thin-wall maps should have this false, since ThinDungeonGenerator (in conjunction with DungeonUtility's 070 * hashesToLines method) makes thin lines for walls that should be displayed as between the boundaries of 071 * other cells. The overlap behavior needed for some "thin enough" cells to be displayed between the cells 072 * can be accomplished by using {@link #setTextSize(float, float)} to double the previously-given cell width 073 * and height. 074 */ 075 public boolean onlyRenderEven; 076 077 /** 078 * Creates a bare-bones panel with all default values for text rendering. 079 * <br> 080 * This uses a default font that is not supplied in the JAR library of SquidLib; you need two files to use it if it 081 * does not render correctly: 082 * <ul> 083 * <li>https://github.com/SquidPony/SquidLib/blob/master/assets/Zodiac-Square-12x12.fnt</li> 084 * <li>https://github.com/SquidPony/SquidLib/blob/master/assets/Zodiac-Square-12x12.png</li> 085 * </ul> 086 * @param gridWidth the number of cells horizontally 087 * @param gridHeight the number of cells vertically 088 */ 089 public SquidPanel(int gridWidth, int gridHeight) { 090 this(gridWidth, gridHeight, new TextCellFactory().defaultSquareFont()); 091 } 092 093 /** 094 * Creates a panel with the given grid and cell size. Uses a default square font. 095 * <br> 096 * This uses a default font that is not supplied in the JAR library of SquidLib; you need two files to use it if it 097 * does not render correctly: 098 * <ul> 099 * <li>https://github.com/SquidPony/SquidLib/blob/master/assets/Zodiac-Square-12x12.fnt</li> 100 * <li>https://github.com/SquidPony/SquidLib/blob/master/assets/Zodiac-Square-12x12.png</li> 101 * </ul> 102 * @param gridWidth the number of cells horizontally 103 * @param gridHeight the number of cells vertically 104 * @param cellWidth the number of horizontal pixels in each cell 105 * @param cellHeight the number of vertical pixels in each cell 106 */ 107 public SquidPanel(int gridWidth, int gridHeight, int cellWidth, int cellHeight) { 108 this(gridWidth, gridHeight, new TextCellFactory().defaultSquareFont().width(cellWidth).height(cellHeight).initBySize()); 109 } 110 111 /** 112 * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images 113 * are being used, a TextCellFactory is still needed to perform sizing and other utility functions. 114 * 115 * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null 116 * then a default one will be created and initialized. 117 * 118 * @param gridWidth the number of cells horizontally 119 * @param gridHeight the number of cells vertically 120 * @param factory the factory to use for cell rendering 121 */ 122 public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory) { 123 this(gridWidth, gridHeight, factory, DefaultResources.getSCC()); 124 } 125 126 /** 127 * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images 128 * are being used, a TextCellFactory is still needed to perform sizing and other utility functions. 129 * 130 * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null 131 * then a default one will be created and initialized. 132 * 133 * @param gridWidth the number of cells horizontally 134 * @param gridHeight the number of cells vertically 135 * @param factory the factory to use for cell rendering 136 * @param center the color center to use. Can be {@code null}, which will use a default 137 */ 138 public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center) { 139 this(gridWidth, gridHeight, factory, center, 0f, 0f); 140 } 141 142 /** 143 * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images 144 * are being used, a TextCellFactory is still needed to perform sizing and other utility functions. 145 * <br> 146 * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null 147 * then a default one will be created and initialized. The xOffset and yOffset arguments are measured in pixels or 148 * whatever sub-cell unit of measure your game uses (world coordinates, in libGDX parlance), and change where the 149 * SquidPanel starts drawing by simply adding to the initial x and y coordinates. 0 and 0 are usually fine. 150 * 151 * @param gridWidth the number of cells horizontally 152 * @param gridHeight the number of cells vertically 153 * @param factory the factory to use for cell rendering 154 * @param center the color center to use. Can be {@code null}, which will use a default 155 * @param xOffset the x offset to start rendering at, in pixels (or some other sub-cell measurement your game uses) 156 * @param yOffset the y offset to start rendering at, in pixels (or some other sub-cell measurement your game uses) 157 */ 158 public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center, 159 float xOffset, float yOffset) { 160 this(gridWidth, gridHeight, factory, center, xOffset, yOffset, null); 161 } 162 /** 163 * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images 164 * are being used, a TextCellFactory is still needed to perform sizing and other utility functions. Importantly, 165 * this constructor takes a 2D char array argument that can be sized differently than the displayed area. The 166 * displayed area is gridWidth by gridHeight in cells, but the actualMap argument can be much larger, and only a 167 * portion will be displayed at a time. This requires some special work with the Camera and Viewports to get working 168 * correctly; in the squidlib module's examples, EverythingDemo may be a good place to see how this can be done. 169 * You can pass null for actualMap, which will simply create a char array to use internally that is exactly 170 * gridWidth by gridHeight, in cells. 171 * <br> 172 * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null 173 * then a default one will be created and initialized. The xOffset and yOffset arguments are measured in pixels or 174 * whatever sub-cell unit of measure your game uses (world coordinates, in libGDX parlance), and change where the 175 * SquidPanel starts drawing by simply adding to the initial x and y coordinates. 0 and 0 are usually fine. 176 * 177 * @param gridWidth the number of cells horizontally 178 * @param gridHeight the number of cells vertically 179 * @param factory the factory to use for cell rendering 180 * @param center the color center to use. Can be {@code null}, which will use a default 181 * @param xOffset the x offset to start rendering at, in pixels (or some other sub-cell measurement your game uses) 182 * @param yOffset the y offset to start rendering at, in pixels (or some other sub-cell measurement your game uses) 183 * @param actualMap will often be a different size than gridWidth by gridHeight, which enables camera scrolling 184 */ 185 public SquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center, 186 float xOffset, float yOffset, char[][] actualMap) { 187 this.setTransform(false); 188 this.gridWidth = gridWidth; 189 this.gridHeight = gridHeight; 190 if(center == null) 191 scc = DefaultResources.getSCC(); 192 else 193 scc = center; 194 if (factory == null) { 195 textFactory = new TextCellFactory(); 196 } 197 else 198 textFactory = factory; 199 if (!textFactory.initialized()) { 200 textFactory.initByFont(); 201 } 202 203 cellWidth = MathUtils.round(textFactory.actualCellWidth); 204 cellHeight = MathUtils.round(textFactory.actualCellHeight); 205 206 if(actualMap == null || actualMap.length <= 0) 207 contents = ArrayTools.fill('\0', gridWidth, gridHeight); 208 else 209 contents = actualMap; 210 colors = ArrayTools.fill(-0x1.0p125F, contents.length, contents[0].length); 211 212 int w = gridWidth * cellWidth; 213 int h = gridHeight * cellHeight; 214 this.xOffset = xOffset; 215 this.yOffset = yOffset; 216 setSize(w, h); 217 animatedEntities = new OrderedSet<>(); 218 autoActors = new OrderedSet<>(); 219 } 220 221 /** 222 * Places the given characters into the grid starting at 0,0. 223 * 224 * @param chars 225 */ 226 public void put(char[][] chars) { 227 put(0, 0, chars); 228 } 229 230 @Override 231 public void put(/* @Nullable */char[][] chars, Color[][] foregrounds) { 232 if (chars == null) { 233 /* Only colors to put */ 234 final int width = foregrounds.length; 235 final int height = width == 0 ? 0 : foregrounds[0].length; 236 for (int x = 0; x < width; x++) { 237 for (int y = 0; y < height; y++) 238 put(x, y, foregrounds[x][y]); 239 } 240 } else 241 put(0, 0, chars, foregrounds); 242 } 243 244 public void put(int xOffset, int yOffset, char[][] chars) { 245 put(xOffset, yOffset, chars, defaultForeground); 246 } 247 248 public void put(int xOffset, int yOffset, char[][] chars, Color[][] foregrounds) { 249 for (int x = xOffset; x < xOffset + chars.length; x++) { 250 for (int y = yOffset; y < yOffset + chars[0].length; y++) { 251 if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input 252 put(x, y, chars[x - xOffset][y - yOffset], foregrounds[x - xOffset][y - yOffset]); 253 } 254 } 255 } 256 } 257 258 public void put(int xOffset, int yOffset, Color[][] foregrounds) { 259 for (int x = xOffset; x < xOffset + foregrounds.length; x++) { 260 for (int y = yOffset; y < yOffset + foregrounds[0].length; y++) { 261 if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input 262 put(x, y, '\0', foregrounds[x - xOffset][y - yOffset]); 263 } 264 } 265 } 266 } 267 268 public void put(int xOffset, int yOffset, char[][] chars, Color foreground) { 269 for (int x = xOffset; x < xOffset + chars.length; x++) { 270 for (int y = yOffset; y < yOffset + chars[0].length; y++) { 271 if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input 272 put(x, y, chars[x - xOffset][y - yOffset], foreground); 273 } 274 } 275 } 276 } 277 278 /** 279 * Puts the given string horizontally with the first character at the given offset. 280 * 281 * Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than 282 * the grid size) will not be shown but will not cause any malfunctions. 283 * 284 * Will use the default color for this component to draw the characters. 285 * 286 * @param xOffset the x coordinate of the first character 287 * @param yOffset the y coordinate of the first character 288 * @param string the characters to be displayed 289 */ 290 public void put(int xOffset, int yOffset, String string) { 291 put(xOffset, yOffset, string, defaultForeground); 292 } 293 294 @Override 295 public void put(int xOffset, int yOffset, IColoredString<? extends Color> cs) { 296 int x = xOffset; 297 for (IColoredString.Bucket<? extends Color> fragment : cs) { 298 final String s = fragment.getText(); 299 final Color color = fragment.getColor(); 300 put(x, yOffset, s, color == null ? getDefaultForegroundColor() : scc.filter(color)); 301 x += s.length(); 302 } 303 } 304 305 @Override 306 public void put(int xOffset, int yOffset, String string, Color foreground) { 307 if(string == null || string.isEmpty()) 308 return; 309 if (string.length() == 1) { 310 put(xOffset, yOffset, string.charAt(0), scc.filter(foreground).toFloatBits()); 311 } 312 else 313 { 314 float enc = scc.filter(foreground).toFloatBits(); 315 for (int i = 0; i < string.length(); i++) { 316 put(xOffset + i, yOffset, string.charAt(i), enc); 317 } 318 } 319 } 320 public void put(int xOffset, int yOffset, String string, float encodedColor) { 321 if(string == null || string.isEmpty()) 322 return; 323 if (string.length() == 1) { 324 put(xOffset, yOffset, string.charAt(0), encodedColor); 325 } 326 else 327 { 328 for (int i = 0; i < string.length(); i++) { 329 put(xOffset + i, yOffset, string.charAt(i), encodedColor); 330 } 331 } 332 } 333 334 public void put(int xOffset, int yOffset, String string, Color foreground, float colorMultiplier) { 335 if (string.length() == 1) { 336 put(xOffset, yOffset, string.charAt(0), foreground, colorMultiplier); 337 } 338 else 339 { 340 for (int i = 0; i < string.length(); i++) { 341 put(xOffset + i, yOffset, string.charAt(i), foreground, colorMultiplier); 342 } 343 } 344 } 345 346 public void put(int xOffset, int yOffset, String string, float encodedColor, float colorMultiplier) { 347 if (string.length() == 1) { 348 put(xOffset, yOffset, string.charAt(0), encodedColor, colorMultiplier); 349 } 350 else 351 { 352 for (int i = 0; i < string.length(); i++) { 353 put(xOffset + i, yOffset, string.charAt(i), encodedColor, colorMultiplier); 354 } 355 } 356 } 357 358 public void put(int xOffset, int yOffset, char[][] chars, Color foreground, float colorMultiplier) { 359 for (int x = xOffset; x < xOffset + chars.length; x++) { 360 for (int y = yOffset; y < yOffset + chars[0].length; y++) { 361 if (x >= 0 && y >= 0 && x < gridWidth && y < gridHeight) {//check for valid input 362 put(x, y, chars[x - xOffset][y - yOffset], foreground, colorMultiplier); 363 } 364 } 365 } 366 } 367 368 /** 369 * Puts the given string horizontally or optionally vertically, with the first character at the given offset. 370 * 371 * Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than 372 * the grid size) will not be shown but will not cause any malfunctions. 373 * 374 * Will use the default color for this component to draw the characters. 375 * 376 * @param xOffset the x coordinate of the first character 377 * @param yOffset the y coordinate of the first character 378 * @param string the characters to be displayed 379 * @param vertical true if the text should be written vertically, from top to bottom 380 */ 381 public void put(int xOffset, int yOffset, String string, boolean vertical) { 382 put(xOffset, yOffset, string, defaultForeground, vertical); 383 } 384 385 /** 386 * Puts the given string horizontally or optionally vertically, with the first character at the given offset. 387 * 388 * Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than 389 * the grid size) will not be shown but will not cause any malfunctions. 390 * 391 * @param xOffset the x coordinate of the first character 392 * @param yOffset the y coordinate of the first character 393 * @param string the characters to be displayed 394 * @param foreground the color to draw the characters 395 * @param vertical true if the text should be written vertically, from top to bottom 396 */ 397 public void put(int xOffset, int yOffset, String string, Color foreground, boolean vertical) { 398 if (vertical) { 399 for (int i = 0; i < string.length(); i++) { 400 put(xOffset, yOffset + i, string.charAt(i), foreground); 401 } 402 } else { 403 put(xOffset, yOffset, string, foreground); 404 } 405 } 406 407 /** 408 * Puts the given string in the chosen direction, with the first character shown (not necessarily the first in the 409 * string) at the given offset. If you use {@link Direction#LEFT}, then this effectively reverses the String and 410 * prints it with the last character of the String at the minimum-x position, which is the same position that the 411 * first character would be if you printed normally or if you gave this RIGHT (x is xOffset, y is yOffset). Giving 412 * UP acts similarly to LEFT, but has the last character at the minimum-y position and has the first character below 413 * it. The diagonals act as you would expect, combining the behavior of one of UP or DOWN with one of LEFT or RIGHT. 414 * <br> 415 * Does not word wrap. Characters that are not renderable (due to being at negative offsets or offsets greater than 416 * the grid size) will not be shown but will not cause any malfunctions. 417 * 418 * @param xOffset the x coordinate of the first character 419 * @param yOffset the y coordinate of the first character 420 * @param string the characters to be displayed 421 * @param foreground the color to draw the characters 422 * @param direction the direction the text should be written in, such as {@link Direction#RIGHT} for normal layout 423 */ 424 public void put(int xOffset, int yOffset, String string, Color foreground, Direction direction) { 425 float enc = scc.filter(foreground).toFloatBits(); 426 switch (direction) 427 { 428 case DOWN: 429 for (int i = 0; i < string.length(); i++) { 430 put(xOffset, yOffset + i, string.charAt(i), enc); 431 } 432 break; 433 case UP: 434 for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) { 435 put(xOffset, yOffset + p, string.charAt(i), enc); 436 } 437 break; 438 case LEFT: 439 for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) { 440 put(xOffset + p, yOffset, string.charAt(i), enc); 441 } 442 break; 443 case DOWN_RIGHT: 444 for (int i = 0; i < string.length(); i++) { 445 put(xOffset + i, yOffset + i, string.charAt(i), enc); 446 } 447 break; 448 case UP_RIGHT: 449 for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) { 450 put(xOffset + i, yOffset + p, string.charAt(i), enc); 451 } 452 break; 453 case UP_LEFT: 454 for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) { 455 put(xOffset + p, yOffset + p, string.charAt(i), enc); 456 } 457 break; 458 case DOWN_LEFT: 459 for (int i = 0, p = string.length() - 1; i < string.length(); i++, p--) { 460 put(xOffset + p, yOffset + i, string.charAt(i), enc); 461 } 462 break; 463 default: 464 for (int i = 0; i < string.length(); i++) { 465 put(xOffset + i, yOffset, string.charAt(i), enc); 466 } 467 } 468 } 469 470 /** 471 * Changes the chars at the edge of the SquidPanel to be a border drawn with box drawing characters in white. 472 */ 473 public void putBorders() 474 { 475 putBorders(SColor.FLOAT_WHITE, null); 476 } 477 /** 478 * Changes the chars at the edge of the SquidPanel to be a border drawn with box drawing characters in the given 479 * Color, which will be run through any IColorCenter this has for filtering. If caption is non-null, then this puts 480 * that String starting at x=1, y=0. 481 * @param color a libGDX Color to use for the borders 482 * @param caption an optional caption that will be drawn at (1, 0). May be null. 483 * @see #putBordersCaptioned(Color, IColoredString) Another method that takes an IColoredString caption 484 */ 485 public void putBorders(Color color, String caption) 486 { 487 putBorders(scc.filter(color).toFloatBits(), caption); 488 } 489 /** 490 * Changes the chars at the edge of the SquidPanel to be a border drawn with box drawing characters in the given 491 * Color, which will be run through any IColorCenter this has for filtering. 492 * @param color a libGDX Color to use for the borders 493 */ 494 public void putBorders(Color color) 495 { 496 putBorders(color, null); 497 } 498 /** 499 * Changes the chars at the edge of the SquidPanel to be a border drawn with box drawing characters in the given 500 * color as a packed float. 501 * @param encodedColor a packed float color to use for the borders, as from {@link Color#toFloatBits()} 502 */ 503 public void putBorders(float encodedColor) 504 { 505 putBorders(encodedColor, null); 506 } 507 /** 508 * Changes the chars at the edge of the SquidPanel to be a border drawn with box drawing characters in the given 509 * color as a packed float. If caption is non-null, then this puts that String starting at x=1, y=0. 510 * @param encodedColor a packed float color to use for the borders, as from {@link Color#toFloatBits()} 511 * @param caption an optional caption that will be drawn at (1, 0). May be null to have no caption. 512 * @see #putBordersCaptioned(float, IColoredString) Another method that takes an IColoredString caption 513 */ 514 public void putBorders(float encodedColor, String caption) 515 { 516 contents[0][0] = '┌'; 517 contents[gridWidth - 1][0] = '┐'; 518 contents[0][gridHeight - 1] = '└'; 519 contents[gridWidth - 1][gridHeight - 1] = '┘'; 520 for (int i = 1; i < gridWidth - 1; i++) { 521 contents[i][0] = '─'; 522 contents[i][gridHeight - 1] = '─'; 523 } 524 for (int y = 1; y < gridHeight - 1; y++) { 525 contents[0][y] = '│'; 526 contents[gridWidth - 1][y] = '│'; 527 colors[0][y] = encodedColor; 528 colors[gridWidth - 1][y] = encodedColor; 529 } 530 for (int y = 1; y < gridHeight - 1; y++) { 531 for (int x = 1; x < gridWidth - 1; x++) { 532 contents[x][y] = ' '; 533 contents[x][y] = ' '; 534 } 535 } 536 for (int i = 0; i < gridWidth; i++) { 537 colors[i][0] = encodedColor; 538 colors[i][gridHeight - 1] = encodedColor; 539 } 540 541 if (caption != null) { 542 put(1, 0, caption, encodedColor); 543 } 544 } 545 546 /** 547 * Changes the chars at the edge of the SquidPanel to be a border drawn with box drawing characters in the given 548 * libGDX Color. If caption is non-null, then this puts that IColoredString starting at x=1, y=0. 549 * @param color a libGDX Color to use for the borders 550 * @param caption an optional caption as an IColoredString that will be drawn at (1, 0). May be null to have no 551 * caption. Will be colored independently from the border lines. 552 */ 553 public void putBordersCaptioned(Color color, IColoredString<Color> caption) 554 { 555 putBordersCaptioned(scc.filter(color).toFloatBits(), caption); 556 } 557 558 /** 559 * Changes the chars at the edge of the SquidPanel to be a border drawn with box drawing characters in the given 560 * color as a packed float. If caption is non-null, then this puts that IColoredString starting at x=1, y=0. 561 * @param encodedColor a packed float color to use for the borders, as from {@link Color#toFloatBits()} 562 * @param caption an optional caption as an IColoredString that will be drawn at (1, 0). May be null to have no 563 * caption. Will be colored independently from the border lines. 564 */ 565 public void putBordersCaptioned(float encodedColor, IColoredString<Color> caption) 566 567 { 568 contents[0][0] = '┌'; 569 contents[gridWidth - 1][0] = '┐'; 570 contents[0][gridHeight - 1] = '└'; 571 contents[gridWidth - 1][gridHeight - 1] = '┘'; 572 for (int i = 1; i < gridWidth - 1; i++) { 573 contents[i][0] = '─'; 574 contents[i][gridHeight - 1] = '─'; 575 } 576 for (int y = 1; y < gridHeight - 1; y++) { 577 contents[0][y] = '│'; 578 contents[gridWidth - 1][y] = '│'; 579 colors[0][y] = encodedColor; 580 colors[gridWidth - 1][y] = encodedColor; 581 } 582 for (int y = 1; y < gridHeight - 1; y++) { 583 for (int x = 1; x < gridWidth - 1; x++) { 584 contents[x][y] = ' '; 585 contents[x][y] = ' '; 586 } 587 } 588 for (int i = 0; i < gridWidth; i++) { 589 colors[i][0] = encodedColor; 590 colors[i][gridHeight - 1] = encodedColor; 591 } 592 593 if (caption != null) { 594 put(1, 0, caption); 595 } 596 597 } 598 599 /** 600 * Erases the entire panel, leaving only a transparent space. 601 */ 602 public void erase() { 603 ArrayTools.fill(contents, ' '); 604 ArrayTools.fill(colors, 0f); 605 } 606 607 @Override 608 public void clear(int x, int y) { 609 put(x, y, ' ', 0f); 610 } 611 612 @Override 613 public void put(int x, int y, Color color) { 614 put(x, y, '\0', color); 615 } 616 617 public void put(int x, int y, float encodedColor) { 618 put(x, y, '\0', encodedColor); 619 } 620 621 public void put(int x, int y, float encodedColor, float colorMultiplier) { 622 put(x, y, '\0', encodedColor, colorMultiplier); 623 } 624 625 public void put(int x, int y, float encodedColor, float colorMultiplier, float mixColor) { 626 put(x, y, '\0', encodedColor, colorMultiplier, mixColor); 627 } 628 @Override 629 public void blend(int x, int y, float color, float mixBy) 630 { 631 colors[x][y] = SColor.lerpFloatColorsBlended(colors[x][y], color, mixBy); 632 } 633 634 public void put(int x, int y, Color color, float colorMultiplier) { 635 put(x, y, '\0', color, colorMultiplier); 636 } 637 638 public void put(int x, int y, Color color, float mixAmount, Color mix) { 639 put(x, y, '\0', color, mixAmount, mix); 640 } 641 642 @Override 643 public void put(int x, int y, char c) { 644 put(x, y, c, defaultForeground); 645 } 646 647 /** 648 * Takes a unicode codepoint for input. 649 * 650 * @param x 651 * @param y 652 * @param code 653 */ 654 public void put(int x, int y, int code) { 655 put(x, y, code, defaultForeground); 656 } 657 658 public void put(int x, int y, int c, Color color) { 659 put(x, y, (char) c, color); 660 } 661 662 /** 663 * Takes a unicode char for input. 664 * 665 * @param x 666 * @param y 667 * @param c 668 * @param color 669 */ 670 @Override 671 public void put(int x, int y, char c, Color color) { 672 if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) { 673 return;//skip if out of bounds 674 } 675 contents[x][y] = c; 676 colors[x][y] = scc.filter(color).toFloatBits(); 677 } 678 /** 679 * Takes a unicode char for input. 680 * 681 * @param x 682 * @param y 683 * @param c 684 * @param encodedColor a float color as produced by {@link SColor#floatGet(float, float, float, float)} 685 */ 686 public void put(int x, int y, char c, float encodedColor) { 687 if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) { 688 return;//skip if out of bounds 689 } 690 contents[x][y] = c; 691 colors[x][y] = encodedColor; 692 } 693 694 /** 695 * Takes a unicode char for input and a color multiplier that determines how much of {@link #lightingColor} will 696 * affect the given encodedColor. The encodedColor is a float that might be produced by {@link Color#toFloatBits()} 697 * or by mixing multiple such floats with {@link SColor#lerpFloatColors(float, float, float)}. 698 * 699 * @param x 700 * @param y 701 * @param c 702 * @param encodedColor a float color as produced by {@link SColor#floatGet(float, float, float, float)} 703 * @param colorMultiplier how much of {@link #lightingColor} to use in place of encodedColor, from 0.0 to 1.0 704 */ 705 public void put(int x, int y, char c, float encodedColor, float colorMultiplier) { 706 if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) { 707 return;//skip if out of bounds 708 } 709 contents[x][y] = c; 710 colors[x][y] = SColor.lerpFloatColors(encodedColor, lightingFloat, colorMultiplier); 711 } 712 713 /** 714 * Intended for colored lighting; takes a unicode char for input and a color multiplier that determines how much of 715 * mixColor will affect encodedColor. Both encodedColor and mixColor are floats that might be produced by 716 * {@link Color#toFloatBits()} or by mixing multiple such floats with 717 * {@link SColor#lerpFloatColors(float, float, float)}; colorMultiplier is a normal float between 0.0f and 1.0f . 718 * 719 * @param x 720 * @param y 721 * @param c 722 * @param encodedColor a float color as produced by {@link SColor#floatGet(float, float, float, float)} 723 * @param colorMultiplier how much of mixColor to use in place of encodedColor, from 0.0 to 1.0 724 * @param mixColor a color to mix with encodedColor, typically as part of colored lighting 725 */ 726 public void put(int x, int y, char c, float encodedColor, float colorMultiplier, float mixColor) { 727 if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) { 728 return;//skip if out of bounds 729 } 730 contents[x][y] = c; 731 colors[x][y] = SColor.lerpFloatColors(encodedColor, mixColor, colorMultiplier); 732 } 733 734 /** 735 * Puts the given character at position x, y, with its color determined by the given color interpolated with 736 * this SquidPanel's lightingColor (default is white light) by the amount specified by colorMultiplier (0.0 737 * causes no change to the given color, 1.0 uses the lightingColor only, and anything between 0 and 1 will 738 * produce some tint to color, and probably cache the produced color in the IColorCenter this uses). 739 */ 740 public void put(int x, int y, char c, Color color, float colorMultiplier) { 741 if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) { 742 return;//skip if out of bounds 743 } 744 contents[x][y] = c; 745 colors[x][y] = scc.lerp(color, lightingColor, colorMultiplier).toFloatBits(); 746 } 747 748 /** 749 * Puts the given character at position x, y, with its color determined by the given color interpolated with 750 * the given mix color by the amount specified by mixAmount (0.0 causes no change to the given color, 1.0 uses mix 751 * only, and anything between 0 and 1 will produce some tint to color, and probably cache the produced color in the 752 * IColorCenter this uses). 753 * <br> 754 * Note, unlike {@link #put(int, int, char, float, float, float)}, this will use the IColorCenter to produce the 755 * finished color, which may be slightly slower if you don't need any of IColorCenter's features, and will use 756 * more memory if many colors are cached, but has the advantage of being able to adjust colors with filters. 757 */ 758 public void put(int x, int y, char c, Color color, float mixAmount, Color mix) { 759 if (x < 0 || x >= contents.length || y < 0 || y >= contents[0].length) { 760 return;//skip if out of bounds 761 } 762 contents[x][y] = c; 763 colors[x][y] = scc.lerp(color, mix, mixAmount).toFloatBits(); 764 } 765 766 @Override 767 public int cellWidth() { 768 return cellWidth; 769 } 770 771 @Override 772 public int cellHeight() { 773 return cellHeight; 774 } 775 776 @Override 777 public int gridHeight() { 778 return gridHeight; 779 } 780 781 @Override 782 public int gridWidth() { 783 return gridWidth; 784 } 785 786 /** 787 * @return The {@link TextCellFactory} backing {@code this}. 788 */ 789 public TextCellFactory getTextCellFactory() { 790 return textFactory; 791 } 792 793 /** 794 * Sets the size of the text in this SquidPanel (but not the size of the cells) to the given width and height in 795 * pixels (which may be stretched by viewports later on, if your program uses them). 796 * @param wide the width of a glyph in pixels 797 * @param high the height of a glyph in pixels 798 * @return this for chaining 799 */ 800 public SquidPanel setTextSize(float wide, float high) 801 { 802 textFactory.tweakHeight(high).tweakWidth(wide).initBySize(); 803 //textFactory.setSmoothingMultiplier((3f + Math.max(cellWidth * 1f / wide, cellHeight * 1f / high)) / 4f); 804 return this; 805 } 806 807 /** 808 * Draws this SquidPanel and any {@link #autoActors} it has, and calls {@link Actor#act(float)} for each 809 * AnimatedEntity this contains in {@link #animatedEntities} or {@link #autoActors}. 810 * <br> 811 * This will set the shader of {@code batch} if using a distance field or MSDF font and the shader is currently not 812 * configured for such a font; it does not reset the shader to the default so that multiple Actors can all use the 813 * same shader and so specific extra glyphs or other items can be rendered after calling draw(). If you need to draw 814 * both a distance field font and full-color art, you should set the shader on the Batch to null when you want to 815 * draw full-color art, and end the Batch between drawing this object and the other art. 816 * @param batch a Batch such as a {@link FilterBatch} that must be between a begin() and end() call; usually done by Stage 817 * @param parentAlpha only used when drawing children of this SquidPanel 818 */ 819 @Override 820 public void draw(Batch batch, float parentAlpha) { 821 textFactory.configureShader(batch); 822 int inc = onlyRenderEven ? 2 : 1, widthInc = inc * cellWidth, heightInc = inc * cellHeight; 823 float screenX = xOffset - (gridOffsetX <= 0 ? 0 : cellWidth) + getX(), 824 screenY_base = 1f + yOffset + (gridOffsetY <= 0 ? 0 : cellHeight) + gridHeight * cellHeight + getY(), screenY; 825 for (int x = Math.max(0, gridOffsetX-1), xx = (gridOffsetX <= 0) ? 0 : -1; xx <= gridWidth && x < contents.length; x += inc, xx += inc, screenX += widthInc) { 826 screenY = screenY_base; 827 for (int y = Math.max(0, gridOffsetY-1), yy = (gridOffsetY <= 0) ? 0 : -1; yy <= gridHeight && y < contents[x].length; y += inc, yy += inc, screenY -= heightInc) { 828 textFactory.draw(batch, contents[x][y], 829 colors[x][y], 830 screenX,// xOffset + /*- getX() + 1f * */ x * cellWidth, 831 screenY // yOffset + /*- getY() + 1f * */ (gridHeight - y) * cellHeight + 1f 832 ); 833 } 834 } 835 super.draw(batch, parentAlpha); 836 int len = animatedEntities.size(); 837 for (int i = 0; i < len; i++) { 838 animatedEntities.getAt(i).actor.act(Gdx.graphics.getDeltaTime()); 839 } 840 len = autoActors.size(); 841 Actor a; 842 for (int i = 0; i < len; i++) { 843 a = autoActors.getAt(i); 844 if(a == null) continue; 845 if(a.isVisible()) 846 drawActor(batch, parentAlpha, a); 847 a.act(Gdx.graphics.getDeltaTime()); 848 } 849 } 850 851 /** 852 * Draws one AnimatedEntity, specifically the Actor it contains. Batch must be between start() and end() 853 * @param batch Must have start() called already but not stop() yet during this frame. 854 * @param parentAlpha This can be assumed to be 1.0f if you don't know it 855 * @param ae The AnimatedEntity to draw; the position to draw ae is stored inside it. 856 */ 857 public void drawActor(Batch batch, float parentAlpha, AnimatedEntity ae) 858 { 859 drawActor(batch, parentAlpha, ae.actor); 860 /* 861 float prevX = ae.actor.getX(), prevY = ae.actor.getY(); 862 ae.actor.setPosition(prevX - gridOffsetX * cellWidth, prevY + gridOffsetY * cellHeight); 863 ae.actor.draw(batch, parentAlpha); 864 ae.actor.setPosition(prevX, prevY); 865 */ 866 } 867 868 /** 869 * Draws one AnimatedEntity, specifically the Actor it contains. Batch must be between start() and end() 870 * @param batch Must have start() called already but not stop() yet during this frame. 871 * @param parentAlpha This can be assumed to be 1.0f if you don't know it 872 * @param ac The Actor to draw; the position to draw ac is modified and reset based on some fields of this object 873 */ 874 public void drawActor(Batch batch, float parentAlpha, Actor ac) 875 { 876 float prevX = ac.getX(), prevY = ac.getY(); 877 ac.setPosition(prevX - gridOffsetX * cellWidth, prevY + gridOffsetY * cellHeight); 878 ac.draw(batch, parentAlpha); 879 ac.setPosition(prevX, prevY); 880 } 881 882 @Override 883 public void setDefaultForeground(Color defaultForeground) { 884 this.defaultForeground = defaultForeground; 885 } 886 887 @Override 888 public Color getDefaultForegroundColor() { 889 return defaultForeground; 890 } 891 892 public AnimatedEntity getAnimatedEntityByCell(int x, int y) { 893 for(AnimatedEntity ae : animatedEntities) 894 { 895 if(ae.gridX == x && ae.gridY == y) 896 return ae; 897 } 898 return null; 899 } 900 901 /** 902 * Create an AnimatedEntity at position x, y, using the char c in the given color. 903 * @param x 904 * @param y 905 * @param c 906 * @param color 907 * @return 908 */ 909 public AnimatedEntity animateActor(int x, int y, char c, Color color) 910 { 911 return animateActor(x, y, false, c, color); 912 /* 913 Actor a = textFactory.makeActor("" + c, color); 914 a.setName("" + c); 915 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 916 917 AnimatedEntity ae = new AnimatedEntity(a, x, y); 918 animatedEntities.add(ae); 919 return ae; 920 */ 921 } 922 923 /** 924 * Create an AnimatedEntity at position x, y, using the char c in the given color. If doubleWidth is true, treats 925 * the char c as the left char to be placed in a grid of 2-char cells. 926 * @param x 927 * @param y 928 * @param doubleWidth 929 * @param c 930 * @param color 931 * @return 932 */ 933 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, char c, Color color) 934 { 935 Actor a = textFactory.makeActor(c, color); 936 a.setName(String.valueOf(c)); 937 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 938 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 939 animatedEntities.add(ae); 940 return ae; 941 942 /* 943 Actor a = textFactory.makeActor("" + c, color); 944 a.setName("" + c); 945 if(doubleWidth) 946 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 947 else 948 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 949 950 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 951 animatedEntities.add(ae); 952 return ae; 953 */ 954 } 955 956 /** 957 * Create an AnimatedEntity at position x, y, using the String s in the given color. 958 * @param x 959 * @param y 960 * @param s 961 * @param color 962 * @return 963 */ 964 public AnimatedEntity animateActor(int x, int y, String s, Color color) 965 { 966 return animateActor(x, y, false, s, color); 967 } 968 969 /** 970 * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats 971 * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells. 972 * @param x 973 * @param y 974 * @param doubleWidth 975 * @param s 976 * @param color 977 * @return 978 */ 979 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Color color) 980 { 981 Actor a = textFactory.makeActor(s, color); 982 a.setName(s); 983 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 984 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 985 animatedEntities.add(ae); 986 return ae; 987 } 988 /** 989 * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats 990 * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells. 991 * @param x 992 * @param y 993 * @param doubleWidth 994 * @param s 995 * @param colors 996 * @return 997 */ 998 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Collection<Color> colors) 999 { 1000 return animateActor(x, y, doubleWidth, s, colors, 2f); 1001 } 1002 /** 1003 * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats 1004 * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells. 1005 * @param x 1006 * @param y 1007 * @param doubleWidth 1008 * @param s 1009 * @param colors 1010 * @param loopTime 1011 * @return 1012 */ 1013 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Collection<Color> colors, float loopTime) 1014 { 1015 Actor a = textFactory.makeActor(s, colors, loopTime, doubleWidth); 1016 a.setName(s); 1017 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1018 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 1019 animatedEntities.add(ae); 1020 return ae; 1021 } 1022 /** 1023 * Create an AnimatedEntity at position x, y, using '^' as its contents, but as an image so it can be rotated. 1024 * Uses the given colors in a looping pattern, that doesn't count as an animation. If doubleWidth is true, treats 1025 * the '^' as starting in the middle of a 2-char cell. 1026 * @param x 1027 * @param y 1028 * @param doubleWidth 1029 * @param colors 1030 * @param loopTime 1031 * @return 1032 */ 1033 public AnimatedEntity directionMarker(int x, int y, boolean doubleWidth, Collection<Color> colors, float loopTime) 1034 { 1035 Actor a = textFactory.makeDirectionMarker(colors, loopTime, doubleWidth); 1036 a.setName("^"); 1037 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1038 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 1039 animatedEntities.add(ae); 1040 return ae; 1041 } 1042 public AnimatedEntity directionMarker(int x, int y, boolean doubleWidth, Color color) 1043 { 1044 Actor a = textFactory.makeDirectionMarker(color); 1045 a.setName("^"); 1046 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1047 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 1048 animatedEntities.add(ae); 1049 return ae; 1050 } 1051 1052 /** 1053 * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which will be 1054 * stretched to fit one cell. 1055 * @param x 1056 * @param y 1057 * @param texture 1058 * @return 1059 */ 1060 public AnimatedEntity animateActor(int x, int y, TextureRegion texture) 1061 { 1062 return animateActor(x, y, false, texture, Color.WHITE); 1063 /* 1064 Actor a = textFactory.makeActor(texture, Color.WHITE); 1065 a.setName(""); 1066 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1067 1068 AnimatedEntity ae = new AnimatedEntity(a, x, y); 1069 animatedEntities.add(ae); 1070 return ae; 1071 */ 1072 } 1073 1074 /** 1075 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 1076 * stretched to fit one cell. 1077 * @param x 1078 * @param y 1079 * @param texture 1080 * @param color 1081 * @return 1082 */ 1083 public AnimatedEntity animateActor(int x, int y, TextureRegion texture, Color color) 1084 { 1085 return animateActor(x, y, false, texture, color); 1086 /* 1087 Actor a = textFactory.makeActor(texture, color); 1088 a.setName(""); 1089 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1090 1091 AnimatedEntity ae = new AnimatedEntity(a, x, y); 1092 animatedEntities.add(ae); 1093 return ae; 1094 */ 1095 } 1096 1097 /** 1098 * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which will be 1099 * stretched to fit one cell, or two cells if doubleWidth is true. 1100 * @param x 1101 * @param y 1102 * @param doubleWidth 1103 * @param texture 1104 * @return 1105 */ 1106 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture) 1107 { 1108 return animateActor(x, y, doubleWidth, texture, Color.WHITE); 1109 /* 1110 Actor a = textFactory.makeActor(texture, Color.WHITE, (doubleWidth ? 2 : 1) * cellWidth, cellHeight); 1111 1112 a.setName(""); 1113 if(doubleWidth) 1114 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1115 else 1116 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1117 1118 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 1119 animatedEntities.add(ae); 1120 return ae; 1121 */ 1122 } 1123 1124 /** 1125 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 1126 * stretched to fit one cell, or two cells if doubleWidth is true. 1127 * @param x 1128 * @param y 1129 * @param doubleWidth 1130 * @param texture 1131 * @param color 1132 * @return 1133 */ 1134 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Color color) { 1135 Actor a = textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight); 1136 a.setName(""); 1137 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1138 /* 1139 if (doubleWidth) 1140 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1141 else 1142 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1143 */ 1144 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 1145 animatedEntities.add(ae); 1146 return ae; 1147 } 1148 1149 /** 1150 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 1151 * stretched to fit one cell, or two cells if doubleWidth is true. 1152 * @param x 1153 * @param y 1154 * @param doubleWidth 1155 * @param texture 1156 * @param colors 1157 * @return 1158 */ 1159 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Collection<Color> colors){ 1160 return animateActor(x, y, doubleWidth, texture, colors, 2f); 1161 } 1162 /** 1163 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 1164 * stretched to fit one cell, or two cells if doubleWidth is true. 1165 * @param x 1166 * @param y 1167 * @param doubleWidth 1168 * @param texture 1169 * @param colors 1170 * @return 1171 */ 1172 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Collection<Color> colors, float loopTime) { 1173 Actor a = textFactory.makeActor(texture, colors, loopTime, doubleWidth, (doubleWidth ? 2 : 1) * cellWidth, cellHeight); 1174 a.setName(""); 1175 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1176 /* 1177 if (doubleWidth) 1178 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1179 else 1180 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1181 */ 1182 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 1183 animatedEntities.add(ae); 1184 return ae; 1185 } 1186 1187 /** 1188 * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which, if and only 1189 * if stretch is true, will be stretched to fit one cell, or two cells if doubleWidth is true. If stretch is false, 1190 * this will preserve the existing size of texture. 1191 * @param x 1192 * @param y 1193 * @param doubleWidth 1194 * @param stretch 1195 * @param texture 1196 * @return 1197 */ 1198 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, boolean stretch, TextureRegion texture) 1199 { 1200 Actor a = (stretch) 1201 ? textFactory.makeActor(texture, Color.WHITE, (doubleWidth ? 2 : 1) * cellWidth, cellHeight) 1202 : textFactory.makeActor(texture, Color.WHITE, texture.getRegionWidth(), texture.getRegionHeight()); 1203 a.setName(""); 1204 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1205 /* 1206 if(doubleWidth) 1207 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1208 else 1209 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1210 */ 1211 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 1212 animatedEntities.add(ae); 1213 return ae; 1214 } 1215 1216 /** 1217 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which, if and only 1218 * if stretch is true, will be stretched to fit one cell, or two cells if doubleWidth is true. If stretch is false, 1219 * this will preserve the existing size of texture. 1220 * @param x 1221 * @param y 1222 * @param doubleWidth 1223 * @param stretch 1224 * @param texture 1225 * @param color 1226 * @return 1227 */ 1228 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, boolean stretch, TextureRegion texture, Color color) { 1229 1230 Actor a = (stretch) 1231 ? textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight) 1232 : textFactory.makeActor(texture, color, texture.getRegionWidth(), texture.getRegionHeight()); 1233 a.setName(""); 1234 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1235 /* 1236 if (doubleWidth) 1237 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1238 else 1239 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 1240 */ 1241 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 1242 animatedEntities.add(ae); 1243 return ae; 1244 } 1245 1246 /** 1247 * Created an Actor from the contents of the given x,y position on the grid. 1248 * @param x 1249 * @param y 1250 * @return 1251 */ 1252 public TextCellFactory.Glyph cellToActor(int x, int y) 1253 { 1254 return cellToActor(x, y, false); 1255 } 1256 1257 /** 1258 * Created an Actor from the contents of the given x,y position on the grid; deleting 1259 * the grid's String content at this cell. 1260 * 1261 * @param x 1262 * @param y 1263 * @param doubleWidth 1264 * @return A fresh {@link Actor}, that has just been added to {@code this}. 1265 */ 1266 public TextCellFactory.Glyph cellToActor(int x, int y, boolean doubleWidth) 1267 { 1268 return createActor(x, y, contents[x][y], colors[x][y], doubleWidth); 1269 } 1270 1271 /** 1272 * Used internally to go between grid positions and world positions. 1273 * @param gridX x on the grid 1274 * @return x in the world 1275 */ 1276 public float worldX(int gridX, boolean doubleWidth) 1277 { 1278 return getX() + (gridX << (doubleWidth ? 1 : 0)) * textFactory.actualCellWidth; 1279 } 1280 /** 1281 * Used internally to go between grid positions and world positions. 1282 * @param gridY y on the grid 1283 * @return y in the world 1284 */ 1285 public float worldY(int gridY) 1286 { 1287 return getY() + (gridHeight - gridY) * textFactory.actualCellHeight; 1288 } 1289 1290 1291 protected /* @Nullable */ TextCellFactory.Glyph createActor(int x, int y, char name, Color color, boolean doubleWidth) { 1292 //final Actor a = textFactory.makeActor(name, scc.filter(color)); 1293 final TextCellFactory.Glyph a = textFactory.glyph(name, scc.filter(color), worldX(x, doubleWidth), worldY(y)); 1294 //a.setName(String.valueOf(name)); 1295 //a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1296 autoActors.add(a); 1297 return a; 1298 } 1299 1300 protected /* @Nullable */ TextCellFactory.Glyph createActor(int x, int y, char name, float encodedColor, boolean doubleWidth) { 1301 //final Actor a = textFactory.makeActor(name, encodedColor); 1302 final TextCellFactory.Glyph a = textFactory.glyph(name, encodedColor, worldX(x, doubleWidth), worldY(y)); 1303 //a.setName(String.valueOf(name)); 1304 //a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 1305 autoActors.add(a); 1306 return a; 1307 } 1308 1309 public float adjustX(float x, boolean doubleWidth) 1310 { 1311 if(doubleWidth) 1312 return x * 2 * cellWidth + getX(); // may need (x - gridOffsetX) instead of x 1313 else 1314 return (x) * cellWidth + getX(); 1315 } 1316 1317 public float adjustY(float y) 1318 { 1319 return (gridHeight - y - 1) * cellHeight + getY() + (textFactory.msdf ? -textFactory.descent : 1 + cellHeight - textFactory.actualCellHeight); // - textFactory.lineHeight //textFactory.lineTweak * 3f 1320 //return (gridHeight - y - 1) * cellHeight + textFactory.getDescent() * 3 / 2f + getY(); 1321 } 1322 1323 /* 1324 public void startAnimation(Actor a, int oldX, int oldY) 1325 { 1326 Coord tmp = Coord.get(oldX, oldY); 1327 1328 tmp.x = Math.round(a.getX() / cellWidth); 1329 tmp.y = gridHeight - Math.round(a.getY() / cellHeight) - 1; 1330 if(tmp.x >= 0 && tmp.x < gridWidth && tmp.y > 0 && tmp.y < gridHeight) 1331 { 1332 } 1333 } 1334 */ 1335 public void recallActor(Actor a, boolean restoreSym) 1336 { 1337 recallActor(a, restoreSym, Math.round((a.getX() - getX()) / cellWidth) + gridOffsetX, 1338 gridHeight - (int)(a.getY() / cellHeight) - 1 + gridOffsetY); 1339 /* 1340 animationCount--; 1341 int x = Math.round((a.getX() - getX()) / cellWidth) + gridOffsetX, 1342 y = gridHeight - (int)(a.getY() / cellHeight) - 1 + gridOffsetY; 1343 if(onlyRenderEven) 1344 { 1345 // this just sets the least significant bit to 0, making any odd numbers even (decrementing) 1346 x &= -2; 1347 y &= -2; 1348 } 1349 String n; 1350 if(restoreSym && x >= 0 && y >= 0 && x < contents.length && y < contents[x].length 1351 && (n = a.getName()) != null && !n.isEmpty()) 1352 { 1353 contents[x][y] = n.charAt(0); 1354 } 1355 removeActor(a); 1356 autoActors.remove(a); 1357 */ 1358 } 1359 1360 public void recallActor(Actor a, boolean restoreSym, int nextX, int nextY) 1361 { 1362 animationCount--; 1363 if(onlyRenderEven) 1364 { 1365 // this just sets the least significant bit to 0, making any odd numbers even (decrementing) 1366 nextX &= -2; 1367 nextY &= -2; 1368 } 1369 String n; 1370 if(restoreSym && nextX >= 0 && nextY >= 0 && nextX < contents.length && nextY < contents[nextX].length 1371 && (n = a.getName()) != null && !n.isEmpty()) 1372 { 1373 contents[nextX][nextY] = n.charAt(0); 1374 } 1375 removeActor(a); 1376 autoActors.remove(a); 1377 } 1378 1379 public void recallActor(TextCellFactory.Glyph a, boolean restoreSym, int nextX, int nextY) 1380 { 1381 animationCount--; 1382 if(a == null) return; // if something has already removed the Glyph, we still reduce animationCount but do nothing more 1383 if(onlyRenderEven) 1384 { 1385 // this just sets the least significant bit to 0, making any odd numbers even (decrementing) 1386 nextX &= -2; 1387 nextY &= -2; 1388 } 1389 if(restoreSym && nextX >= 0 && nextY >= 0 && nextX < contents.length && nextY < contents[nextX].length) 1390 { 1391 contents[nextX][nextY] = a.shown; 1392 } 1393 removeActor(a); 1394 autoActors.remove(a); 1395 } 1396 1397 public void recallActor(AnimatedEntity ae) 1398 { 1399 recallActor(ae, ae.doubleWidth 1400 ? Math.round((ae.actor.getX() - getX()) / (2 * cellWidth)) + gridOffsetX 1401 : Math.round((ae.actor.getX() - getX()) / cellWidth) + gridOffsetY, 1402 gridHeight - (int)((ae.actor.getY() - getY()) / cellHeight) - 1 + gridOffsetY); 1403// if(ae.doubleWidth) 1404// ae.gridX = Math.round((ae.actor.getX() - getX()) / (2 * cellWidth)) + gridOffsetX; 1405// else 1406// ae.gridX = Math.round((ae.actor.getX() - getX()) / cellWidth) + gridOffsetY; 1407// ae.gridY = gridHeight - (int)((ae.actor.getY() - getY()) / cellHeight) - 1 + gridOffsetY; 1408// if(onlyRenderEven) 1409// { 1410// // this just sets the least significant bit to 0, making any odd numbers even (decrementing) 1411// ae.gridX &= -2; 1412// ae.gridY &= -2; 1413// } 1414// ae.animating = false; 1415// animationCount--; 1416 } 1417 public void recallActor(AnimatedEntity ae, int nextX, int nextY) 1418 { 1419 ae.gridX = nextX; 1420 ae.gridY = nextY; 1421 if(onlyRenderEven) 1422 { 1423 // this just sets the least significant bit to 0, making any odd numbers even (decrementing) 1424 ae.gridX &= -2; 1425 ae.gridY &= -2; 1426 } 1427 //fixPosition(ae); 1428 ae.animating = false; 1429 animationCount--; 1430 } 1431 1432 public void fixPosition(AnimatedEntity ae) 1433 { 1434 ae.actor.setPosition(adjustX(ae.gridX, ae.doubleWidth), adjustY(ae.gridY)); 1435 } 1436 public void fixPositions() 1437 { 1438 for (int i = 0; i < animatedEntities.size(); i++) { 1439 AnimatedEntity ae = animatedEntities.getAt(i); 1440 ae.actor.setPosition(adjustX(ae.gridX, ae.doubleWidth), adjustY(ae.gridY)); 1441 } 1442 } 1443 1444 /** 1445 * Start a bumping animation in the given direction that will last duration seconds. 1446 * @param ae an AnimatedEntity returned by animateActor() 1447 * @param direction 1448 * @param duration a float, measured in seconds, for how long the animation should last; commonly 0.12f 1449 */ 1450 public void bump(final AnimatedEntity ae, Direction direction, float duration) 1451 { 1452 final Actor a = ae.actor; 1453 final float x = adjustX(ae.gridX, ae.doubleWidth), 1454 y = adjustY(ae.gridY); 1455 if(a == null || ae.animating) return; 1456 duration = clampDuration(duration); 1457 animationCount++; 1458 ae.animating = true; 1459 a.addAction(Actions.sequence( 1460 Actions.moveToAligned(x + direction.deltaX * cellWidth * 0.35f, y - direction.deltaY * cellHeight * 0.35f, 1461 Align.bottomLeft, duration * 0.35F), 1462 Actions.moveToAligned(x, y, Align.bottomLeft, duration * 0.65F), 1463 Actions.delay(duration, Actions.run(new Runnable() { 1464 @Override 1465 public void run() { 1466 recallActor(ae, ae.gridX, ae.gridY); 1467 } 1468 })))); 1469 1470 } 1471 /** 1472 * Start a bumping animation in the given direction that will last duration seconds. 1473 * @param x 1474 * @param y 1475 * @param direction 1476 * @param duration a float, measured in seconds, for how long the animation should last; commonly 0.12f 1477 */ 1478 public void bump(final int x, final int y, Direction direction, float duration) 1479 { 1480 final Actor a = cellToActor(x, y); 1481 if(a == null) return; 1482 duration = clampDuration(duration); 1483 animationCount++; 1484 float nextX = adjustX(x, false), nextY = adjustY(y); 1485 /* 1486 x *= cellWidth; 1487 y = (gridHeight - y - 1); 1488 y *= cellHeight; 1489 y -= 1; 1490 x += getX(); 1491 y += getY(); 1492 */ 1493 a.addAction(Actions.sequence( 1494 Actions.moveToAligned(nextX + direction.deltaX * cellWidth * 0.35f, nextY + direction.deltaY * cellHeight * 0.35f, 1495 Align.bottomLeft, duration * 0.35F), 1496 Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration * 0.65F), 1497 Actions.delay(duration, Actions.run(new Runnable() { 1498 @Override 1499 public void run() { 1500 recallActor(a, true, x, y); 1501 } 1502 })))); 1503 1504 } 1505 1506 /** 1507 * Starts a bumping animation in the direction provided. 1508 * 1509 * @param x 1510 * @param y 1511 * @param direction 1512 */ 1513 public void bump(int x, int y, Direction direction) { 1514 bump(x, y, direction, DEFAULT_ANIMATION_DURATION); 1515 } 1516 /** 1517 * Starts a bumping animation in the direction provided. 1518 * 1519 * @param location 1520 * @param direction 1521 */ 1522 public void bump(Coord location, Direction direction) { 1523 bump(location.x, location.y, direction, DEFAULT_ANIMATION_DURATION); 1524 } 1525 /** 1526 * Start a movement animation for the object at the grid location x, y and moves it to newX, newY over a number of 1527 * seconds given by duration (often 0.12f or somewhere around there). 1528 * @param ae an AnimatedEntity returned by animateActor() 1529 * @param newX 1530 * @param newY 1531 * @param duration 1532 */ 1533 public void slide(final AnimatedEntity ae, final int newX, final int newY, float duration) 1534 { 1535 final Actor a = ae.actor; 1536 final float nextX = adjustX(newX, ae.doubleWidth), nextY = adjustY(newY); 1537 if(a == null || ae.animating) return; 1538 duration = clampDuration(duration); 1539 animationCount++; 1540 ae.animating = true; 1541 ae.gridX = newX; 1542 ae.gridY = newY; 1543 a.addAction(Actions.sequence( 1544 Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration), 1545 Actions.delay(duration, Actions.run(new Runnable() { 1546 @Override 1547 public void run() { 1548 recallActor(ae, newX, newY); 1549 } 1550 })))); 1551 } 1552 1553 /** 1554 * Start a movement animation for the object at the grid location x, y and moves it to newX, newY over a number of 1555 * seconds given by duration (often 0.12f or somewhere around there). 1556 * @param x 1557 * @param y 1558 * @param newX 1559 * @param newY 1560 * @param duration 1561 */ 1562 public void slide(int x, int y, final int newX, final int newY, float duration) 1563 { 1564 final Actor a = cellToActor(x, y); 1565 if(a == null) return; 1566 duration = clampDuration(duration); 1567 animationCount++; 1568 float nextX = adjustX(newX, false), nextY = adjustY(newY); 1569 1570 /* 1571 newX *= cellWidth; 1572 newY = (gridHeight - newY - 1); 1573 newY *= cellHeight; 1574 newY -= 1; 1575 x += getX(); 1576 y += getY(); 1577 */ 1578 a.addAction(Actions.sequence( 1579 Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration), 1580 Actions.delay(duration, Actions.run(new Runnable() { 1581 @Override 1582 public void run() { 1583 recallActor(a, true, newX, newY); 1584 } 1585 })))); 1586 } 1587 1588 /** 1589 * Slides {@code name} from {@code (x,y)} to {@code (newx, newy)}. If 1590 * {@code name} or {@code 1591 * color} is {@code null}, it is picked from this panel (hereby removing the 1592 * current name, if any). 1593 * 1594 * @param x 1595 * Where to start the slide, horizontally. 1596 * @param y 1597 * Where to start the slide, vertically. 1598 * @param name 1599 * The name to slide, or {@code null} to pick it from this 1600 * panel's {@code (x,y)} cell. 1601 * @param color 1602 * The color to use, or {@code null} to pick it from this panel's 1603 * {@code (x,y)} cell. 1604 * @param newX 1605 * Where to end the slide, horizontally. 1606 * @param newY 1607 * Where to end the slide, vertically. 1608 * @param duration 1609 * The animation's duration. 1610 */ 1611 public void slide(int x, int y, final /* @Nullable */ String name, /* @Nullable */ Color color, int newX, 1612 int newY, float duration) { 1613 slide(x, y, name, color, newX, newY, duration, null); 1614 } 1615 1616 /** 1617 * Slides {@code name} from {@code (x,y)} to {@code (newx, newy)}. If 1618 * {@code name} or {@code color} is {@code null}, it is picked from this 1619 * panel (thereby removing the current name, if any). This also allows 1620 * a Runnable to be given as {@code postRunnable} to be run after the 1621 * slide completes. 1622 * 1623 * @param x 1624 * Where to start the slide, horizontally. 1625 * @param y 1626 * Where to start the slide, vertically. 1627 * @param name 1628 * The name to slide, or {@code null} to pick it from this 1629 * panel's {@code (x,y)} cell. 1630 * @param color 1631 * The color to use, or {@code null} to pick it from this panel's 1632 * {@code (x,y)} cell. 1633 * @param newX 1634 * Where to end the slide, horizontally. 1635 * @param newY 1636 * Where to end the slide, vertically. 1637 * @param duration 1638 * The animation's duration. 1639 * @param postRunnable a Runnable to execute after the slide completes; may be null to do nothing. 1640 */ 1641 public void slide(int x, int y, final /* @Nullable */ String name, /* @Nullable */ Color color, final int newX, 1642 final int newY, float duration, /* @Nullable */ Runnable postRunnable) { 1643 if(name != null && name.isEmpty()) 1644 return; 1645 final TextCellFactory.Glyph a = createActor(x, y, name == null ? contents[x][y] : name.charAt(0), 1646 color == null ? colors[x][y] : color.toFloatBits(), false); 1647 if (a == null) 1648 return; 1649 1650 duration = clampDuration(duration); 1651 animationCount++; 1652 1653 final int nbActions = 2 + (postRunnable == null ? 0 : 1); 1654 1655 int index = 0; 1656 final Action[] sequence = new Action[nbActions]; 1657 final float nextX = adjustX(newX, false); 1658 final float nextY = adjustY(newY); 1659 sequence[index++] = Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration); 1660 if(postRunnable != null) 1661 { 1662 sequence[index++] = Actions.run(postRunnable); 1663 } 1664 /* Do this one last, so that hasActiveAnimations() returns true during 'postRunnables' */ 1665 sequence[index] = Actions.delay(duration, Actions.run(new Runnable() { 1666 @Override 1667 public void run() { 1668 recallActor(a, name == null, newX, newY); 1669 } 1670 })); 1671 1672 a.addAction(Actions.sequence(sequence)); 1673 } 1674 1675 1676 /** 1677 * Starts a movement animation for the object at the given grid location at the default speed. 1678 * 1679 * @param start Coord to pick up a tile from and slide 1680 * @param end Coord to end the slide on 1681 */ 1682 public void slide(Coord start, Coord end) { 1683 slide(start.x, start.y, end.x, end.y, DEFAULT_ANIMATION_DURATION); 1684 } 1685 1686 /** 1687 * Starts a movement animation for the object at the given grid location at the default speed for one grid square in 1688 * the direction provided. 1689 * 1690 * @param start Coord to pick up a tile from and slide 1691 * @param direction Direction enum that indicates which way the slide should go 1692 */ 1693 public void slide(Coord start, Direction direction) { 1694 slide(start.x, start.y, start.x + direction.deltaX, start.y + direction.deltaY, DEFAULT_ANIMATION_DURATION); 1695 } 1696 1697 /** 1698 * Starts a sliding movement animation for the object at the given location at the provided speed. The duration is 1699 * how many seconds should pass for the entire animation. 1700 * 1701 * @param start Coord to pick up a tile from and slide 1702 * @param end Coord to end the slide on 1703 * @param duration in seconds, as a float 1704 */ 1705 public void slide(Coord start, Coord end, float duration) { 1706 slide(start.x, start.y, end.x, end.y, duration); 1707 } 1708 1709 /** 1710 * Starts an wiggling animation for the object at the given location for the given duration in seconds. 1711 * 1712 * @param ae an AnimatedEntity returned by animateActor() 1713 * @param duration in seconds, as a float 1714 */ 1715 public void wiggle(final AnimatedEntity ae, float duration) { 1716 1717 final Actor a = ae.actor; 1718 final float x = adjustX(ae.gridX, ae.doubleWidth), y = adjustY(ae.gridY); 1719 //final int x = ae.gridX * cellWidth * ((ae.doubleWidth) ? 2 : 1) + (int)getX(), y = (gridHeight - ae.gridY - 1) * cellHeight - 1 + (int)getY(); 1720 if(a == null || ae.animating) 1721 return; 1722 duration = clampDuration(duration); 1723 ae.animating = true; 1724 animationCount++; 1725 StatefulRNG gRandom = DefaultResources.getGuiRandom(); 1726 a.addAction(Actions.sequence( 1727 Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f, 1728 y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f, 1729 Align.bottomLeft, duration * 0.2F), 1730 Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f, 1731 y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f, 1732 Align.bottomLeft, duration * 0.2F), 1733 Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f, 1734 y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f, 1735 Align.bottomLeft, duration * 0.2F), 1736 Actions.moveToAligned(x + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f, 1737 y + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f, 1738 Align.bottomLeft, duration * 0.2F), 1739 Actions.moveToAligned(x, y, Align.bottomLeft, duration * 0.2F), 1740 Actions.delay(duration, Actions.run(new Runnable() { 1741 @Override 1742 public void run() { 1743 recallActor(ae, ae.gridX, ae.gridY); 1744 } 1745 })))); 1746 } 1747 /** 1748 * Starts an wiggling animation for the object at the given location for the given duration in seconds. 1749 * 1750 * @param x 1751 * @param y 1752 * @param duration 1753 */ 1754 public void wiggle(final int x, final int y, float duration) { 1755 final Actor a = cellToActor(x, y); 1756 if(a == null) return; 1757 duration = clampDuration(duration); 1758 animationCount++; 1759 float nextX = adjustX(x, false), nextY = adjustY(y); 1760 /* 1761 x *= cellWidth; 1762 y = (gridHeight - y - 1); 1763 y *= cellHeight; 1764 y -= 1; 1765 x += getX(); 1766 y += getY(); 1767 */ 1768 StatefulRNG gRandom = DefaultResources.getGuiRandom(); 1769 a.addAction(Actions.sequence( 1770 Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f, 1771 nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f, 1772 Align.bottomLeft, duration * 0.2F), 1773 Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f, 1774 nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f, 1775 Align.bottomLeft, duration * 0.2F), 1776 Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f, 1777 nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f, 1778 Align.bottomLeft, duration * 0.2F), 1779 Actions.moveToAligned(nextX + (gRandom.nextFloat() - 0.5F) * cellWidth * 0.4f, 1780 nextY + (gRandom.nextFloat() - 0.5F) * cellHeight * 0.4f, 1781 Align.bottomLeft, duration * 0.2F), 1782 Actions.moveToAligned(nextX, nextY, Align.bottomLeft, duration * 0.2F), 1783 Actions.delay(duration, Actions.run(new Runnable() { 1784 @Override 1785 public void run() { 1786 recallActor(a, true, x, y); 1787 } 1788 })))); 1789 } 1790 1791 /** 1792 * Starts a tint animation for {@code ae} for the given {@code duration} in seconds. 1793 * 1794 * @param ae an AnimatedEntity returned by animateActor() 1795 * @param color what to transition ae's color towards, and then transition back from 1796 * @param duration how long the total "round-trip" transition should take in seconds 1797 */ 1798 public void tint(final AnimatedEntity ae, Color color, float duration) { 1799 final Actor a = ae.actor; 1800 if(a == null) 1801 return; 1802 duration = clampDuration(duration); 1803 ae.animating = true; 1804 animationCount++; 1805 Color ac = scc.filter(a.getColor()); 1806 a.addAction(Actions.sequence( 1807 Actions.color(color, duration * 0.3f), 1808 Actions.color(ac, duration * 0.7f), 1809 Actions.delay(duration, Actions.run(new Runnable() { 1810 @Override 1811 public void run() { 1812 recallActor(ae, ae.gridX, ae.gridY); 1813 } 1814 })))); 1815 } 1816 1817 /** 1818 * Like {@link #tint(int, int, Color, float)}, but waits for {@code delay} 1819 * (in seconds) before performing it. 1820 * @param delay how long to wait in seconds before starting the effect 1821 * @param x the x-coordinate of the cell to tint 1822 * @param y the y-coordinate of the cell to tint 1823 * @param color what to transition ae's color towards, and then transition back from 1824 * @param duration how long the total "round-trip" transition should take in seconds 1825 */ 1826 public void tint(float delay, int x, int y, Color color, float duration) { 1827 tint(delay, x, y, color, duration, null); 1828 } 1829 1830 /** 1831 * Like {@link #tint(int, int, Color, float)}, but waits for {@code delay} 1832 * (in seconds) before performing it. Additionally, enqueue {@code postRunnable} 1833 * for running after the created action ends. 1834 * @param delay how long to wait in seconds before starting the effect 1835 * @param x the x-coordinate of the cell to tint 1836 * @param y the y-coordinate of the cell to tint 1837 * @param color what to transition ae's color towards, and then transition back from 1838 * @param duration how long the total "round-trip" transition should take in seconds 1839 * @param postRunnable a Runnable to execute after the tint completes; may be null to do nothing. 1840 */ 1841 1842 public void tint(float delay, final int x, final int y, Color color, float duration, Runnable postRunnable) { 1843 final TextCellFactory.Glyph a = cellToActor(x, y); 1844 if(a == null) return; 1845 duration = clampDuration(duration); 1846 animationCount++; 1847 1848 float ac = a.getPackedColor(), c = color.toFloatBits(); 1849 final int nbActions = 3 + (0 < delay ? 1 : 0) + (postRunnable == null ? 0 : 1); 1850 final Action[] sequence = new Action[nbActions]; 1851 int index = 0; 1852 if (0 < delay) 1853 sequence[index++] = Actions.delay(delay); 1854 sequence[index++] = PackedColorAction.color(c, duration * 0.3f); 1855 sequence[index++] = PackedColorAction.color(ac, duration * 0.7f); 1856 if(postRunnable != null) 1857 { 1858 sequence[index++] = Actions.run(postRunnable); 1859 } 1860 /* Do this one last, so that hasActiveAnimations() returns true during 'postRunnable' */ 1861 sequence[index] = Actions.run(new Runnable() { 1862 @Override 1863 public void run() { 1864 recallActor(a, true, x, y); 1865 } 1866 }); 1867 1868 a.addAction(Actions.sequence(sequence)); 1869 } 1870 1871 /** 1872 * Starts a tint animation for the object at {@code (x,y)} for the given 1873 * {@code duration} (in seconds). 1874 * @param x the x-coordinate of the cell to tint 1875 * @param y the y-coordinate of the cell to tint 1876 * @param color 1877 * @param duration 1878 */ 1879 public void tint(int x, int y, Color color, float duration) { 1880 tint(0f, x, y, color, duration); 1881 } 1882 1883 /** 1884 * Fade the cell at {@code (x,y)} to {@code color}. Contrary to 1885 * {@link #tint(int, int, Color, float)}, this action does not restore the 1886 * cell's color at the end of its execution. This is for example useful to 1887 * fade the game screen when the rogue dies. 1888 * 1889 * @param x the x-coordinate of the cell to tint 1890 * @param y the y-coordinate of the cell to tint 1891 * @param color 1892 * The color at the end of the fadeout. 1893 * @param duration 1894 * The fadeout's duration. 1895 */ 1896 public void fade(final int x, final int y, Color color, float duration) { 1897 final Actor a = cellToActor(x, y); 1898 if (a == null) 1899 return; 1900 duration = clampDuration(duration); 1901 animationCount++; 1902 final Color c = scc.filter(color); 1903 a.addAction(Actions.sequence(Actions.color(c, duration), Actions.run(new Runnable() { 1904 @Override 1905 public void run() { 1906 recallActor(a, true, x, y); 1907 } 1908 }))); 1909 } 1910 1911 /** 1912 * Create a new Actor at (x, y) that looks like glyph but can rotate, and immediately starts changing color from 1913 * startColor to endColor and changing rotation from startRotation to endRotation, taking duration seconds to 1914 * complete before removing the Actor. 1915 * @param x the x position in cells; doesn't change 1916 * @param y the y position in cells; doesn't change 1917 * @param glyph the char to show (the same char throughout the effect, but it can rotate) 1918 * @param startColor the starting Color 1919 * @param endColor the Color to transition to 1920 * @param startRotation the amount of rotation, in degrees, the glyph should start at 1921 * @param endRotation the amount of rotation, in degrees, the glyph should end at 1922 * @param duration the duration in seconds for the effect 1923 */ 1924 public void summon(int x, int y, char glyph, Color startColor, Color endColor, 1925 float startRotation, float endRotation, float duration) 1926 { 1927 summon(x, y, x, y, glyph, startColor, endColor, false, startRotation, endRotation, duration); 1928 } 1929 /** 1930 * Create a new Actor at (startX, startY) that looks like glyph but can rotate, sets its color, and immediately 1931 * starts changing position so it ends on the cell (endX, endY) and changing rotation from startRotation to 1932 * endRotation, taking duration seconds to complete before removing the Actor. 1933 * @param startX the starting x position in cells 1934 * @param startY the starting y position in cells 1935 * @param endX the ending x position in cells 1936 * @param endY the ending y position in cells 1937 * @param glyph the char to show (the same char throughout the effect, but it can rotate) 1938 * @param color the Color of the glyph throughout the effect 1939 * @param startRotation the amount of rotation, in degrees, the glyph should start at 1940 * @param endRotation the amount of rotation, in degrees, the glyph should end at 1941 * @param duration the duration in seconds for the effect 1942 */ 1943 public void summon(int startX, int startY, int endX, int endY, char glyph, Color color, 1944 float startRotation, float endRotation, float duration) 1945 { 1946 summon(startX, startY, endX, endY, glyph, color, color, false, startRotation, endRotation, duration); 1947 } 1948 /** 1949 * Create a new Actor at (startX, startY) that looks like glyph but has the given rotation, and immediately starts 1950 * changing color from startColor to endColor, and changing position so it ends on the cell (endX, endY), taking 1951 * duration seconds to complete before removing the Actor. 1952 * @param startX the starting x position in cells 1953 * @param startY the starting y position in cells 1954 * @param endX the ending x position in cells 1955 * @param endY the ending y position in cells 1956 * @param glyph the char to show (the same char throughout the effect, but it can rotate) 1957 * @param startColor the starting Color 1958 * @param endColor the Color to transition to 1959 * @param rotation the amount of rotation, in degrees, the glyph should have throughout the effect 1960 * @param duration the duration in seconds for the effect 1961 */ 1962 public void summon(int startX, int startY, int endX, int endY, char glyph, Color startColor, Color endColor, 1963 float rotation, float duration) 1964 { 1965 summon(startX, startY, endX, endY, glyph, startColor, endColor, false, rotation, rotation, duration); 1966 } 1967 /** 1968 * Create a new Actor at (startX, startY) that looks like glyph but can rotate, and immediately starts changing 1969 * color from startColor to endColor, changing position so it ends on the cell (endX, endY), and changing rotation 1970 * from startRotation to endRotation, taking duration seconds to complete before removing the Actor. 1971 * @param startX the starting x position in cells 1972 * @param startY the starting y position in cells 1973 * @param endX the ending x position in cells 1974 * @param endY the ending y position in cells 1975 * @param glyph the char to show (the same char throughout the effect, but it can rotate) 1976 * @param startColor the starting Color 1977 * @param endColor the Color to transition to 1978 * @param startRotation the amount of rotation, in degrees, the glyph should start at 1979 * @param endRotation the amount of rotation, in degrees, the glyph should end at 1980 * @param duration the duration in seconds for the effect 1981 */ 1982 public void summon(int startX, int startY, int endX, int endY, char glyph, Color startColor, Color endColor, 1983 float startRotation, float endRotation, float duration) 1984 { 1985 summon(startX, startY, endX, endY, glyph, startColor, endColor, false, startRotation, endRotation, duration); 1986 } 1987 /** 1988 * Create a new Actor at (startX, startY) that looks like glyph but can rotate, and immediately starts changing 1989 * color from startColor to endColor, changing position so it ends on the cell (endX, endY), and changing rotation 1990 * from startRotation to endRotation, taking duration seconds to complete before removing the Actor. Allows 1991 * setting doubleWidth, which centers the created Actor in the space between the two glyphs in a cell. 1992 * @param startX the starting x position in cells 1993 * @param startY the starting y position in cells 1994 * @param endX the ending x position in cells 1995 * @param endY the ending y position in cells 1996 * @param glyph the char to show (the same char throughout the effect, but it can rotate) 1997 * @param startColor the starting Color 1998 * @param endColor the Color to transition to 1999 * @param doubleWidth true if this uses double-width cells, false in most cases 2000 * @param startRotation the amount of rotation, in degrees, the glyph should start at 2001 * @param endRotation the amount of rotation, in degrees, the glyph should end at 2002 * @param duration the duration in seconds for the effect 2003 */ 2004 public void summon(int startX, int startY, int endX, int endY, char glyph, Color startColor, Color endColor, boolean doubleWidth, 2005 float startRotation, float endRotation, float duration) 2006 { 2007 summon(0f, startX, startY, endX, endY, glyph, startColor, endColor, doubleWidth, startRotation, endRotation, duration); 2008 } 2009 /** 2010 * Create a new Actor at (startX, startY) that looks like glyph but can rotate, and immediately starts changing 2011 * color from startColor to endColor, changing position so it ends on the cell (endX, endY), and changing rotation 2012 * from startRotation to endRotation, taking duration seconds to complete before removing the Actor. Allows 2013 * setting doubleWidth, which centers the created Actor in the space between the two glyphs in a cell. 2014 * @param delay amount of time, in seconds, to wait before starting the effect 2015 * @param startX the starting x position in cells 2016 * @param startY the starting y position in cells 2017 * @param endX the ending x position in cells 2018 * @param endY the ending y position in cells 2019 * @param glyph the char to show (the same char throughout the effect, but it can rotate) 2020 * @param startColor the starting Color 2021 * @param endColor the Color to transition to 2022 * @param doubleWidth true if this uses double-width cells, false in most cases 2023 * @param startRotation the amount of rotation, in degrees, the glyph should start at 2024 * @param endRotation the amount of rotation, in degrees, the glyph should end at 2025 * @param duration the duration in seconds for the effect 2026 */ 2027 public void summon(float delay, final int startX, final int startY, final int endX, final int endY, 2028 final char glyph, final Color startColor, final Color endColor, final boolean doubleWidth, 2029 final float startRotation, final float endRotation, float duration) 2030 2031 { 2032 final float dur = clampDuration(duration); 2033 animationCount++; 2034 final Action[] sequence = new Action[2]; 2035 if (0 < delay) { 2036 addAction(Actions.delay(delay, Actions.run(new Runnable() { 2037 @Override 2038 public void run() { 2039 final ColorChangeImage 2040 gi = textFactory.makeGlyphImage(glyph, scc.gradient(startColor, endColor, (int) (dur * 40)), dur * 1.1f, doubleWidth); 2041 gi.setPosition(adjustX(startX, doubleWidth), adjustY(startY)); 2042 gi.setRotation(startRotation); 2043 autoActors.add(gi); 2044 sequence[0] = Actions.parallel( 2045 Actions.moveTo(adjustX(endX, doubleWidth), adjustY(endY), dur), 2046 Actions.rotateTo(endRotation, dur)); 2047 sequence[1] = Actions.run(new Runnable() { 2048 @Override 2049 public void run() { 2050 recallActor(gi, false); 2051 } 2052 }); 2053 2054 gi.addAction(Actions.sequence(sequence)); 2055 2056 } 2057 }))); 2058 } 2059 else { 2060 final ColorChangeImage 2061 gi = textFactory.makeGlyphImage(glyph, scc.gradient(startColor, endColor, (int) (dur * 40)), dur * 1.1f, doubleWidth); 2062 gi.setPosition(adjustX(startX, doubleWidth), adjustY(startY)); 2063 gi.setRotation(startRotation); 2064 autoActors.add(gi); 2065 sequence[0] = Actions.parallel( 2066 Actions.moveTo(adjustX(endX, doubleWidth), adjustY(endY), dur), 2067 Actions.rotateTo(endRotation, dur)); 2068 sequence[1] = Actions.run(new Runnable() { 2069 @Override 2070 public void run() { 2071 recallActor(gi, false); 2072 } 2073 }); 2074 gi.addAction(Actions.sequence(sequence)); 2075 } 2076 } 2077 /** 2078 * Convenience method to produce an explosion, splash, or burst effect. Calls 2079 * {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with 2080 * different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation. 2081 * This overload always moves Actors 1 cell away, which is a safe default, uses a "normal" amount of rotation for 2082 * for all of the actors (a value of 1f if you used another overload), and always uses an end color that is a 2083 * modified copy of startColor with 0 alpha (making the Actors all fade to transparent). The parameter 2084 * eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions. 2085 * @param x the starting, center, x-position to create all Actors at 2086 * @param y the starting, center, y-position to create all Actors at 2087 * @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond 2088 * @param glyph the char to make a rotate-able Actor of; should definitely be visible 2089 * @param startColor the color to start the effect with 2090 * @param duration how long, in seconds, the effect should last 2091 */ 2092 2093 public void burst(int x, int y, boolean eightWay, char glyph, 2094 Color startColor, 2095 float duration) 2096 { 2097 burst(0f, x, y, 1, eightWay, glyph, startColor, startColor.cpy().sub(0,0,0,1), false, 1f, duration); 2098 } 2099 2100 2101 /** 2102 * Convenience method to produce an explosion, splash, or burst effect. Calls 2103 * {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with 2104 * different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation. 2105 * This overload always moves Actors 1 cell away, which is a safe default, and uses a "normal" amount of rotation 2106 * for all of the actors (a value of 1f if you used another overload). The parameter 2107 * eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions. 2108 * @param x the starting, center, x-position to create all Actors at 2109 * @param y the starting, center, y-position to create all Actors at 2110 * @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond 2111 * @param glyph the char to make a rotate-able Actor of; should definitely be visible 2112 * @param startColor the color to start the effect with 2113 * @param endColor the color to end the effect on 2114 * @param duration how long, in seconds, the effect should last 2115 */ 2116 public void burst(int x, int y, boolean eightWay, char glyph, 2117 Color startColor, Color endColor, 2118 float duration) 2119 { 2120 burst(0f, x, y, 1, eightWay, glyph, startColor, endColor, false, 1f, duration); 2121 } 2122 2123 /** 2124 * Convenience method to produce an explosion, splash, or burst effect. Calls 2125 * {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with 2126 * different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation. 2127 * This overload always moves Actors 1 cell away, which is a safe default. Some parameters need explanation: 2128 * eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions; 2129 * rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to 2130 * start at the correct rotation and not change that rotation over the course of the effect, but can be between 0 2131 * and 1 or higher than 1 (negative values may also work). 2132 * @param x the starting, center, x-position to create all Actors at 2133 * @param y the starting, center, y-position to create all Actors at 2134 * @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond 2135 * @param glyph the char to make a rotate-able Actor of; should definitely be visible 2136 * @param startColor the color to start the effect with 2137 * @param endColor the color to end the effect on 2138 * @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation 2139 * @param duration how long, in seconds, the effect should last 2140 */ 2141 public void burst(int x, int y, boolean eightWay, char glyph, 2142 Color startColor, Color endColor, 2143 float rotationStrength, float duration) 2144 { 2145 burst(0f, x, y, 1, eightWay, glyph, startColor, endColor, false, rotationStrength, duration); 2146 } 2147 2148 2149 /** 2150 * Convenience method to produce an explosion, splash, or burst effect. Calls 2151 * {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with 2152 * different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation. 2153 * Some parameters need explanation: distance is how many cells away to move the created Actors away from (x,y); 2154 * eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions; 2155 * rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to 2156 * start at the correct rotation and not change that rotation over the course of the effect, but can be between 0 2157 * and 1 or higher than 1 (negative values may also work). 2158 * @param x the starting, center, x-position to create all Actors at 2159 * @param y the starting, center, y-position to create all Actors at 2160 * @param distance how far away, in cells, to move each actor from the center (Chebyshev distance, forming a square) 2161 * @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond 2162 * @param glyph the char to make a rotate-able Actor of; should definitely be visible 2163 * @param startColor the color to start the effect with 2164 * @param endColor the color to end the effect on 2165 * @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation 2166 * @param duration how long, in seconds, the effect should last 2167 */ 2168 public void burst(int x, int y, int distance, boolean eightWay, char glyph, 2169 Color startColor, Color endColor, 2170 float rotationStrength, float duration) 2171 { 2172 burst(0f, x, y, distance, eightWay, glyph, startColor, endColor, false, rotationStrength, duration); 2173 } 2174 2175 2176 /** 2177 * Convenience method to produce an explosion, splash, or burst effect. Calls 2178 * {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with 2179 * different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation. 2180 * This overload always moves Actors 1 cell away, which is a safe default. Some parameters need explanation: 2181 * eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions; 2182 * rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to 2183 * start at the correct rotation and not change that rotation over the course of the effect, but can be between 0 2184 * and 1 or higher than 1 (negative values may also work). 2185 * @param x the starting, center, x-position to create all Actors at 2186 * @param y the starting, center, y-position to create all Actors at 2187 * @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond 2188 * @param glyph the char to make a rotate-able Actor of; should definitely be visible 2189 * @param startColor the color to start the effect with 2190 * @param endColor the color to end the effect on 2191 * @param doubleWidth true if this should use the double-width-cell technique, false in most cases 2192 * @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation 2193 * @param duration how long, in seconds, the effect should last 2194 */ 2195 public void burst(int x, int y, boolean eightWay, char glyph, 2196 Color startColor, Color endColor, boolean doubleWidth, 2197 float rotationStrength, float duration) 2198 { 2199 burst(0f, x, y, 1, eightWay, glyph, startColor, endColor, doubleWidth, rotationStrength, duration); 2200 } 2201 2202 /** 2203 * Convenience method to produce an explosion, splash, or burst effect. Calls 2204 * {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with 2205 * different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation. 2206 * Some parameters need explanation: distance is how many cells away to move the created Actors away from (x,y); 2207 * eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions; 2208 * rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to 2209 * start at the correct rotation and not change that rotation over the course of the effect, but can be between 0 2210 * and 1 or higher than 1 (negative values may also work). 2211 * @param x the starting, center, x-position to create all Actors at 2212 * @param y the starting, center, y-position to create all Actors at 2213 * @param distance how far away, in cells, to move each actor from the center (Chebyshev distance, forming a square) 2214 * @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond 2215 * @param glyph the char to make a rotate-able Actor of; should definitely be visible 2216 * @param startColor the color to start the effect with 2217 * @param endColor the color to end the effect on 2218 * @param doubleWidth true if this should use the double-width-cell technique, false in most cases 2219 * @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation 2220 * @param duration how long, in seconds, the effect should last 2221 */ 2222 public void burst(int x, int y, int distance, boolean eightWay, char glyph, 2223 Color startColor, Color endColor, boolean doubleWidth, 2224 float rotationStrength, float duration) 2225 { 2226 burst(0f, x, y, distance, eightWay, glyph, startColor, endColor, doubleWidth, rotationStrength, duration); 2227 } 2228 2229 /** 2230 * Convenience method to produce an explosion, splash, or burst effect. Calls 2231 * {@link #summon(float, int, int, int, int, char, Color, Color, boolean, float, float, float)} repeatedly with 2232 * different parameters. As with summon(), this creates temporary Actors that change color, position, and rotation. 2233 * Some parameters need explanation: distance is how many cells away to move the created Actors away from (x,y); 2234 * eightWay determines whether this produces 4 (cardinal) or 8 (cardinal and diagonal) rotations and directions; 2235 * rotationStrength can default to 1 if you want some rotation (which looks good) or 0 if you want the Actors to 2236 * start at the correct rotation and not change that rotation over the course of the effect, but can be between 0 2237 * and 1 or higher than 1 (negative values may also work). 2238 * @param delay amount of time, in seconds, to wait before starting the effect 2239 * @param x the starting, center, x-position to create all Actors at 2240 * @param y the starting, center, y-position to create all Actors at 2241 * @param distance how far away, in cells, to move each actor from the center (Chebyshev distance, forming a square) 2242 * @param eightWay if true, creates 8 Actors and moves them away in a square, otherwise, 4 Actors in a diamond 2243 * @param glyph the char to make a rotate-able Actor of; should definitely be visible 2244 * @param startColor the color to start the effect with 2245 * @param endColor the color to end the effect on 2246 * @param doubleWidth true if this should use the double-width-cell technique, false in most cases 2247 * @param rotationStrength how strongly to rotate the Actors; 0 is no rotation, 1 is a normal rotation 2248 * @param duration how long, in seconds, the effect should last 2249 */ 2250 public void burst(float delay, int x, int y, int distance, boolean eightWay, char glyph, 2251 Color startColor, Color endColor, boolean doubleWidth, 2252 float rotationStrength, float duration) 2253 { 2254 Direction d; 2255 if(eightWay) 2256 { 2257 for (int i = 0; i < 8; i++) { 2258 d = Direction.CLOCKWISE[i]; 2259 summon(delay, x, y, x - d.deltaX * distance, y + d.deltaY * distance, 2260 glyph, startColor, endColor, doubleWidth, 2261 45f * i, 45f * (i - rotationStrength), 2262 duration); 2263 } 2264 } 2265 else 2266 { 2267 for (int i = 0; i < 4; i++) { 2268 d = Direction.CARDINALS_CLOCKWISE[i]; 2269 summon(delay, x, y, x - d.deltaX * distance, y + d.deltaY * distance, 2270 glyph, startColor, endColor, doubleWidth, 2271 90f * i, 90f * (i - rotationStrength), 2272 duration); 2273 } 2274 2275 } 2276 } 2277 2278 @Override 2279 public boolean hasActiveAnimations() { 2280 return 0 < animationCount || 0 < getActions().size; 2281 } 2282 2283 public OrderedSet<AnimatedEntity> getAnimatedEntities() { 2284 return animatedEntities; 2285 } 2286 2287 public void removeAnimatedEntity(AnimatedEntity ae) 2288 { 2289 animatedEntities.remove(ae); 2290 } 2291 2292 /** 2293 * @return The current color center. Never {@code null}. 2294 */ 2295 public IColorCenter<Color> getColorCenter() { 2296 return scc; 2297 } 2298 2299 /** 2300 * Use this method if you use your own {@link IColorCenter} and want this 2301 * panel not to allocate its own colors (or fill 2302 * {@link DefaultResources#getSCC()} but instead to the provided center. 2303 * 2304 * @param scc 2305 * The color center to use. Should not be {@code null}. 2306 * @throws NullPointerException 2307 * If {@code scc} is {@code null}. 2308 */ 2309 public void setColorCenter(IColorCenter<Color> scc) { 2310 if (scc == null) 2311 /* Better fail now than later */ 2312 throw new NullPointerException( 2313 "The color center should not be null in " + getClass().getSimpleName()); 2314 this.scc = scc; 2315 } 2316 2317 public char getAt(int x, int y) 2318 { 2319 return contents[x][y]; 2320 } 2321 public Color getColorAt(int x, int y) 2322 { 2323 return SColor.colorFromFloat(tmpColor, colors[x][y]); 2324 } 2325 2326 public Color getLightingColor() { 2327 return lightingColor; 2328 } 2329 2330 public void setLightingColor(Color lightingColor) { 2331 this.lightingColor = lightingColor; 2332 lightingFloat = lightingColor.toFloatBits(); 2333 } 2334 2335 protected float clampDuration(float duration) { 2336 return Math.max(duration, 0.02f); 2337 } 2338 2339 /** 2340 * The X offset that the whole panel's internals will be rendered at. If the {@code gridWidth} of this SquidPanel is 2341 * less than the actual size of the char[][] it renders, then you can use gridOffsetX to start rendering at a 2342 * different position 2343 * @return the current offset in cells along the x axis 2344 */ 2345 public int getGridOffsetX() { 2346 return gridOffsetX; 2347 } 2348 2349 /** 2350 * Sets the X offset that the whole panel's internals will be rendered at. 2351 * @param gridOffsetX the requested offset in cells 2352 */ 2353 public void setGridOffsetX(int gridOffsetX) { 2354 this.gridOffsetX = clamp(gridOffsetX,0, contents.length - gridWidth); 2355 } 2356 2357 /** 2358 * The Y offset that the whole panel's internals will be rendered at. 2359 * @return the current offset in cells along the y axis 2360 */ 2361 public int getGridOffsetY() { 2362 return gridOffsetY; 2363 } 2364 2365 /** 2366 * Sets the Y offset that the whole panel's internals will be rendered at. 2367 * @param gridOffsetY the requested offset in cells 2368 */ 2369 public void setGridOffsetY(int gridOffsetY) { 2370 this.gridOffsetY = clamp(gridOffsetY,0, contents[0].length - gridHeight); 2371 } 2372 2373 /** 2374 * The number of cells along the x-axis that will be rendered of this panel. 2375 * @return the number of cells along the x-axis that will be rendered of this panel 2376 */ 2377 public int getGridWidth() { 2378 return gridWidth; 2379 } 2380 2381 /** 2382 * Sets the number of cells along the x-axis that will be rendered of this panel to gridWidth. 2383 * @param gridWidth the requested width in cells 2384 */ 2385 public void setGridWidth(int gridWidth) { 2386 this.gridWidth = gridWidth; 2387 } 2388 2389 /** 2390 * The number of cells along the y-axis that will be rendered of this panel 2391 * @return the number of cells along the y-axis that will be rendered of this panel 2392 */ 2393 public int getGridHeight() { 2394 return gridHeight; 2395 } 2396 2397 /** 2398 * Sets the number of cells along the y-axis that will be rendered of this panel to gridHeight. 2399 * @param gridHeight the requested height in cells 2400 */ 2401 public void setGridHeight(int gridHeight) { 2402 this.gridHeight = gridHeight; 2403 } 2404 2405 /** 2406 * Gets the total number of cells along the x-axis that this stores; this is usually equivalent to 2407 * {@link #getGridWidth()}, but not if the constructor 2408 * {@link #SquidPanel(int, int, TextCellFactory, IColorCenter, float, float, char[][])} was used to set a 2409 * larger-than-normal map. 2410 * @return the width of the internal array this can render, which may be larger than the visible width 2411 */ 2412 public int getTotalWidth() 2413 { 2414 return contents.length; 2415 } 2416 /** 2417 * Gets the total number of cells along the y-axis that this stores; this is usually equivalent to 2418 * {@link #getGridHeight()}, but not if the constructor 2419 * {@link #SquidPanel(int, int, TextCellFactory, IColorCenter, float, float, char[][])} was used to set a 2420 * larger-than-normal map. 2421 * @return the height of the internal array this can render, which may be larger than the visible height 2422 */ 2423 public int getTotalHeight() 2424 { 2425 return contents[0].length; 2426 } 2427 2428 /** 2429 * Sets the position of the actor's bottom left corner. 2430 * 2431 * @param x 2432 * @param y 2433 */ 2434 @Override 2435 public void setPosition(float x, float y) { 2436 super.setPosition(x, y); 2437 setBounds(x, y, getWidth(), getHeight()); 2438 } 2439 2440 public float getxOffset() { 2441 return xOffset; 2442 } 2443 2444 public void setOffsetX(float xOffset) { 2445 this.xOffset = xOffset; 2446 } 2447 2448 public float getyOffset() { 2449 return yOffset; 2450 } 2451 2452 public void setOffsetY(float yOffset) { 2453 this.yOffset = yOffset; 2454 } 2455 2456 public void setOffsets(float x, float y) { 2457 xOffset = x; 2458 yOffset = y; 2459 } 2460 2461 /** 2462 * Gets the status of a boolean flag used for rendering thin maps; it will almost always be false unless it 2463 * was set to true with {@link #setOnlyRenderEven(boolean)}. 2464 * <br> 2465 * This is meant for thin-wall maps, where only cells where x and y are both even numbers have backgrounds 2466 * displayed. Should be false when using this SquidPanel for anything that isn't specifically a background 2467 * of a map that uses the thin-wall method from ThinDungeonGenerator or something similar. Even the 2468 * foregrounds of thin-wall maps should have this false, since ThinDungeonGenerator (in conjunction with 2469 * DungeonUtility's hashesToLines() method) makes thin lines for walls that should be displayed as between 2470 * the boundaries of other cells. The overlap behavior needed for some "thin enough" cells to be displayed 2471 * between the cells can be accomplished by using {@link #setTextSize(float, float)} to double the 2472 * previously-given cell width and height. 2473 * 2474 * @return the current status of the onlyRenderEven flag, which defaults to false 2475 */ 2476 public boolean getOnlyRenderEven() { 2477 return onlyRenderEven; 2478 } 2479 /** 2480 * Sets the status of a boolean flag used for rendering thin maps; it should almost always be the default, 2481 * which is false, unless you are using a thin-wall map, and then this should be true only if this 2482 * SquidPanel is used for the background layer. 2483 * <br> 2484 * This is meant for thin-wall maps, where only cells where x and y are both even numbers have backgrounds 2485 * displayed. Should be false when using this SquidPanel for anything that isn't specifically a background 2486 * of a map that uses the thin-wall method from ThinDungeonGenerator or something similar. Even the 2487 * foregrounds of thin-wall maps should have this false, since ThinDungeonGenerator (in conjunction with 2488 * DungeonUtility's hashesToLines() method) makes thin lines for walls that should be displayed as between 2489 * the boundaries of other cells. The overlap behavior needed for some "thin enough" cells to be displayed 2490 * between the cells can be accomplished by using {@link #setTextSize(float, float)} to double the 2491 * previously-given cell width and height. 2492 * 2493 * @param onlyRenderEven generally, should only be true if this SquidPanel is a background of a thin map 2494 */ 2495 2496 public void setOnlyRenderEven(boolean onlyRenderEven) { 2497 this.onlyRenderEven = onlyRenderEven; 2498 } 2499 2500 /** 2501 * Gets a "snapshot" of the data represented by this SquidPanel; stores the dimensions, the char data, and the color 2502 * data in a way that can be set back to a SquidPanel using {@link #setFromSnapshot(String, int, int, int, int)} or 2503 * its overload that takes a StringBuilder. The actual contents of the returned StringBuilder are unlikely to be 2504 * legible in any way if read as text, and are meant to be concise and stable across versions. 2505 * @return a StringBuilder representation of this SquidPanel's data that can be passed later to {@link #setFromSnapshot(StringBuilder, int, int, int, int)} or converted to String and passed to its overload 2506 */ 2507 public StringBuilder getSnapshot() 2508 { 2509 return getSnapshot(0, 0, gridWidth, gridHeight); 2510 } 2511 /** 2512 * Gets a "snapshot" of the data represented by this SquidPanel; stores the dimensions, the char data, and the color 2513 * data in a way that can be set back to a SquidPanel using {@link #setFromSnapshot(String, int, int, int, int)} or 2514 * its overload that takes a StringBuilder. The actual contents of the returned StringBuilder are unlikely to be 2515 * legible in any way if read as text, and are meant to be concise and stable across versions. This overload allows 2516 * the first x and y position used to be specified, as well as the width and height to use (the actual width and 2517 * height stored may be different if this SquidPanel's gridWidth and/or gridHeight are smaller than the width and/or 2518 * height given). 2519 * @param startX the first x position to use in the snapshot, inclusive 2520 * @param startY the first y position to use in the snapshot, inclusive 2521 * @param width how wide the snapshot area should be; x positions from startX to startX + width - 1 will be used 2522 * @param height how tall the snapshot area should be; y positions from startY to startY + height - 1 will be used 2523 * @return a StringBuilder representation of this SquidPanel's data that can be passed later to {@link #setFromSnapshot(StringBuilder, int, int, int, int)} or converted to String and passed to its overload 2524 */ 2525 public StringBuilder getSnapshot(int startX, int startY, int width, int height) { 2526 width = Math.min(gridWidth - startX, width); 2527 height = Math.min(gridHeight - startY, height); 2528 StringBuilder sb = new StringBuilder(width * height * 9 + 12); 2529 sb.append(width).append('x').append(height).append(':'); 2530 for (int x = startX, i = 0; i < width; x++, i++) { 2531 sb.append(contents[x], startY, height); 2532 } 2533 char[] reuse = new char[8]; 2534 for (int x = startX, i = 0; i < width; x++, i++) { 2535 for (int y = startY, j = 0; j < height; y++, j++) { 2536 sb.append(SColor.floatToChars(reuse, colors[x][y])); 2537 } 2538 } 2539 return sb; 2540 } 2541 2542 /** 2543 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 2544 * SquidPanel from 0,0 (inclusive) up to the dimensions stored in the snapshot to match the snapshot's data. 2545 * @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)} 2546 * @return this after setting, for chaining 2547 */ 2548 public SquidPanel setFromSnapshot(StringBuilder snapshot) 2549 { 2550 return setFromSnapshot(snapshot, 0, 0, -1, -1); 2551 } 2552 /** 2553 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 2554 * SquidPanel from the position given by putX,putY (inclusive) up to the dimensions stored in the snapshot 2555 * (considering putX and putY as offsets) so they have the values stored in the snapshot. 2556 * @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)} 2557 * @param putX where to start placing the data from the snapshot, x position 2558 * @param putY where to start placing the data from the snapshot, y position 2559 * @return this after setting, for chaining 2560 */ 2561 public SquidPanel setFromSnapshot(StringBuilder snapshot, int putX, int putY) 2562 { 2563 return setFromSnapshot(snapshot, putX, putY, -1, -1); 2564 } 2565 /** 2566 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 2567 * SquidPanel from the position given by putX,putY (inclusive) to putX+limitWidth,putY+limitHeight (exclusive) so 2568 * they have the values stored in the snapshot. If limitWidth or limitHeight is negative, this uses the full width 2569 * and height of the snapshot (stopping early if it would extend past the gridWidth or gridHeight of this 2570 * SquidPanel). 2571 * @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)} 2572 * @param putX where to start placing the data from the snapshot, x position 2573 * @param putY where to start placing the data from the snapshot, y position 2574 * @param limitWidth if negative, uses all of snapshot's width as possible, otherwise restricts the width allowed 2575 * @param limitHeight if negative, uses all of snapshot's height as possible, otherwise restricts the height allowed 2576 * @return this after setting, for chaining 2577 */ 2578 public SquidPanel setFromSnapshot(StringBuilder snapshot, int putX, int putY, int limitWidth, int limitHeight) 2579 { 2580 if(putX >= gridWidth || putY >= gridHeight || snapshot == null || snapshot.length() < 4) return this; 2581 if(putX < 0) putX = 0; 2582 if(putY < 0) putY = 0; 2583 int start = snapshot.indexOf(":")+1, width = StringKit.intFromDec(snapshot), 2584 height = StringKit.intFromDec(snapshot, snapshot.indexOf("x") + 1, start), 2585 run = start; 2586 if(limitWidth < 0) 2587 limitWidth = Math.min(width, gridWidth - putX); 2588 else 2589 limitWidth = Math.min(limitWidth, Math.min(width, gridWidth - putX)); 2590 2591 if(limitHeight < 0) 2592 limitHeight = Math.min(height, gridHeight - putY); 2593 else 2594 limitHeight = Math.min(limitHeight, Math.min(height, gridHeight - putY)); 2595 for (int x = putX, i = 0; i < limitWidth; x++, i++, run += height) { 2596 snapshot.getChars(run, run + limitHeight, contents[x], putY); 2597 } 2598 run = start + width * height; 2599 for (int x = putX, i = 0; i < limitWidth; x++, i++) { 2600 for (int y = putY, j = 0; j < limitHeight; y++, j++) { 2601 colors[x][y] = SColor.charsToFloat(snapshot, run); 2602 run += 8; 2603 } 2604 } 2605 return this; 2606 } 2607 2608 /** 2609 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 2610 * SquidPanel from 0,0 (inclusive) up to the dimensions stored in the snapshot to match the snapshot's data. 2611 * <br> 2612 * This overload takes a String instead of a StringBuilder for potentially-easier loading from files. 2613 * @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)} 2614 * @return this after setting, for chaining 2615 */ 2616 public SquidPanel setFromSnapshot(String snapshot) 2617 { 2618 return setFromSnapshot(snapshot, 0, 0, -1, -1); 2619 } 2620 /** 2621 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 2622 * SquidPanel from the position given by putX,putY (inclusive) up to the dimensions stored in the snapshot 2623 * (considering putX and putY as offsets) so they have the values stored in the snapshot. 2624 * <br> 2625 * This overload takes a String instead of a StringBuilder for potentially-easier loading from files. 2626 * @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)} 2627 * @param putX where to start placing the data from the snapshot, x position 2628 * @param putY where to start placing the data from the snapshot, y position 2629 * @return this after setting, for chaining 2630 */ 2631 public SquidPanel setFromSnapshot(String snapshot, int putX, int putY) 2632 { 2633 return setFromSnapshot(snapshot, putX, putY, -1, -1); 2634 } 2635 2636 /** 2637 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 2638 * SquidPanel from the position given by putX,putY (inclusive) to putX+limitWidth,putY+limitHeight (exclusive) so 2639 * they have the values stored in the snapshot. If limitWidth or limitHeight is negative, this uses the full width 2640 * and height of the snapshot (stopping early if it would extend past the gridWidth or gridHeight of this 2641 * SquidPanel). 2642 * <br> 2643 * This overload takes a String instead of a StringBuilder for potentially-easier loading from files. 2644 * @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)} 2645 * @param putX where to start placing the data from the snapshot, x position 2646 * @param putY where to start placing the data from the snapshot, y position 2647 * @param limitWidth if negative, uses all of snapshot's width as possible, otherwise restricts the width allowed 2648 * @param limitHeight if negative, uses all of snapshot's height as possible, otherwise restricts the height allowed 2649 * @return this after setting, for chaining 2650 */ 2651 2652 public SquidPanel setFromSnapshot(String snapshot, int putX, int putY, int limitWidth, int limitHeight) 2653 { 2654 if(putX >= gridWidth || putY >= gridHeight || snapshot == null || snapshot.length() < 4) return this; 2655 if(putX < 0) putX = 0; 2656 if(putY < 0) putY = 0; 2657 int start = snapshot.indexOf(":")+1, width = StringKit.intFromDec(snapshot), 2658 height = StringKit.intFromDec(snapshot, snapshot.indexOf("x") + 1, start), 2659 run = start; 2660 if(limitWidth < 0) 2661 limitWidth = Math.min(width, gridWidth - putX); 2662 else 2663 limitWidth = Math.min(limitWidth, Math.min(width, gridWidth - putX)); 2664 2665 if(limitHeight < 0) 2666 limitHeight = Math.min(height, gridHeight - putY); 2667 else 2668 limitHeight = Math.min(limitHeight, Math.min(height, gridHeight - putY)); 2669 for (int x = putX, i = 0; i < limitWidth; x++, i++, run += height) { 2670 snapshot.getChars(run, run + limitHeight, contents[x], putY); 2671 } 2672 run = start + width * height; 2673 for (int x = putX, i = 0; i < limitWidth; x++, i++) { 2674 for (int y = putY, j = 0; j < limitHeight; y++, j++) { 2675 colors[x][y] = SColor.charsToFloat(snapshot, run); 2676 run += 8; 2677 } 2678 } 2679 return this; 2680 } 2681}