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.scenes.scene2d.Actor; 008import com.badlogic.gdx.utils.IntMap; 009import squidpony.IColorCenter; 010import squidpony.StringKit; 011 012import java.util.ArrayList; 013import java.util.Collection; 014 015/** 016 * Displays text and images in a grid pattern, like SquidPanel, but will automatically render certain chars as images. 017 * Supports basic animations, such as sliding, wiggling, or fading the contents of a cell. 018 * <br> 019 * Grid width and height settings are in terms of number of cells. Cell width and height are in terms of number of 020 * pixels (when there is no stretching taking place due to viewport or window size). 021 * <br> 022 * @author Eben Howard - http://squidpony.com - howard@squidpony.com 023 * @author Tommy Ettinger 024 */ 025public class ImageSquidPanel extends SquidPanel { 026 027 public IntMap<TextureRegion> imageMap; 028 029 /** 030 * Creates a bare-bones panel with all default values for text rendering. 031 * 032 * @param gridWidth the number of cells horizontally 033 * @param gridHeight the number of cells vertically 034 */ 035 public ImageSquidPanel(int gridWidth, int gridHeight) { 036 this(gridWidth, gridHeight, new TextCellFactory().defaultSquareFont()); 037 } 038 039 /** 040 * Creates a panel with the given grid and cell size. Uses a default square font. 041 * 042 * @param gridWidth the number of cells horizontally 043 * @param gridHeight the number of cells vertically 044 * @param cellWidth the number of horizontal pixels in each cell 045 * @param cellHeight the number of vertical pixels in each cell 046 */ 047 public ImageSquidPanel(int gridWidth, int gridHeight, int cellWidth, int cellHeight) { 048 this(gridWidth, gridHeight, new TextCellFactory().defaultSquareFont().width(cellWidth).height(cellHeight).initBySize()); 049 } 050 051 /** 052 * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images 053 * are being used, a TextCellFactory is still needed to perform sizing and other utility functions. 054 * 055 * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null 056 * then a default one will be created and initialized. 057 * 058 * @param gridWidth the number of cells horizontally 059 * @param gridHeight the number of cells vertically 060 * @param factory the factory to use for cell rendering 061 */ 062 public ImageSquidPanel(int gridWidth, int gridHeight, TextCellFactory factory) { 063 this(gridWidth, gridHeight, factory, DefaultResources.getSCC()); 064 } 065 066 /** 067 * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images 068 * are being used, a TextCellFactory is still needed to perform sizing and other utility functions. 069 * 070 * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null 071 * then a default one will be created and initialized. 072 * 073 * @param gridWidth the number of cells horizontally 074 * @param gridHeight the number of cells vertically 075 * @param factory the factory to use for cell rendering 076 * @param center 077 * The color center to use. Can be {@code null}, but then must be set later on with 078 * {@link SquidPanel#setColorCenter(IColorCenter)}. 079 */ 080 public ImageSquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center) { 081 this(gridWidth, gridHeight, factory, center, 0f, 0f); 082 } 083 084 /** 085 * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images 086 * are being used, a TextCellFactory is still needed to perform sizing and other utility functions. 087 * 088 * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null 089 * then a default one will be created and initialized. 090 * 091 * @param gridWidth the number of cells horizontally 092 * @param gridHeight the number of cells vertically 093 * @param factory the factory to use for cell rendering 094 * @param center 095 * The color center to use. Can be {@code null}, but then must be set later on with 096 * {@link SquidPanel#setColorCenter(IColorCenter)}. 097 */ 098 public ImageSquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center, 099 float xOffset, float yOffset) { 100 this(gridWidth, gridHeight, factory, center, xOffset, yOffset, null); 101 } 102 /** 103 * Builds a panel with the given grid size and all other parameters determined by the factory. Even if sprite images 104 * are being used, a TextCellFactory is still needed to perform sizing and other utility functions. Importantly, 105 * this constructor takes a 2D char array argument that can be sized differently than the displayed area. The 106 * displayed area is gridWidth by gridHeight in cells, but the actualMap argument can be much larger, and only a 107 * portion will be displayed at a time. This requires some special work with the Camera and Viewports to get working 108 * correctly; in the squidlib module's examples, EverythingDemo may be a good place to see how this can be done. 109 * You can pass null for actualMap, which will simply create a char array to use internally that is exactly 110 * gridWidth by gridHeight, in cells. 111 * <br> 112 * If the TextCellFactory has not yet been initialized, then it will be sized at 12x12 px per cell. If it is null 113 * then a default one will be created and initialized. The xOffset and yOffset arguments are measured in pixels or 114 * whatever sub-cell unit of measure your game uses (world coordinates, in libGDX parlance), and change where the 115 * SquidPanel starts drawing by simply adding to the initial x and y coordinates. 0 and 0 are usually fine. 116 * 117 * @param gridWidth the number of cells horizontally 118 * @param gridHeight the number of cells vertically 119 * @param factory the factory to use for cell rendering 120 * @param center 121 * The color center to use. Can be {@code null}, but then must be set later on with 122 * {@link SquidPanel#setColorCenter(IColorCenter)}. 123 * @param xOffset the x offset to start rendering at, in pixels (or some other sub-cell measurement your game uses) 124 * @param yOffset the y offset to start rendering at, in pixels (or some other sub-cell measurement your game uses) 125 * @param actualMap will often be a different size than gridWidth by gridHeight, which enables camera scrolling 126 */ 127 public ImageSquidPanel(int gridWidth, int gridHeight, TextCellFactory factory, IColorCenter<Color> center, 128 float xOffset, float yOffset, char[][] actualMap) { 129 super(gridWidth, gridHeight, factory, center, xOffset, yOffset, actualMap); 130 imageMap = new IntMap<>(128); 131 } 132 /** 133 * Draws this ImageSquidPanel and any {@link #autoActors} it has, and calls {@link Actor#act(float)} for each 134 * AnimatedEntity this contains in {@link #animatedEntities} or {@link #autoActors}. 135 * <br> 136 * This will set the shader of {@code batch} if using a distance field or MSDF font and the shader is currently not 137 * configured for such a font; it does not reset the shader to the default so that multiple Actors can all use the 138 * same shader and so specific extra glyphs or other items can be rendered after calling draw(). If you need to draw 139 * both a distance field font and full-color art, you should set the shader on the Batch to null when you want to 140 * draw full-color art, and end the Batch between drawing this object and the other art. 141 * @param batch a Batch such as a {@link FilterBatch} that must be between a begin() and end() call; usually done by Stage 142 * @param parentAlpha only used when drawing children of this ImageSquidPanel 143 */ 144 @Override 145 public void draw (Batch batch, float parentAlpha) { 146 textFactory.configureShader(batch); 147 int inc = onlyRenderEven ? 2 : 1, widthInc = inc * cellWidth, heightInc = inc * cellHeight; 148 TextureRegion tr; 149 float screenX = xOffset - (gridOffsetX <= 0 ? 0 : cellWidth) + getX(), 150 screenY_base = 1f + yOffset + (gridOffsetY <= 0 ? 0 : cellHeight) + gridHeight * cellHeight + getY(), screenY; 151 char c; 152 for (int x = Math.max(0, gridOffsetX-1), xx = (gridOffsetX <= 0) ? 0 : -1; xx <= gridWidth && x < contents.length; x += inc, xx += inc, screenX += widthInc) { 153 screenY = screenY_base; 154 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) { 155 c = contents[x][y]; 156 tr = imageMap.get(c); 157 if(tr == null) 158 textFactory.draw(batch, c, colors[x][y], screenX, screenY); 159 else 160 textFactory.draw(batch, tr, colors[x][y], screenX, screenY); 161 } 162 } 163 super.draw(batch, parentAlpha); 164 int len = animatedEntities.size(); 165 for (int i = 0; i < len; i++) { 166 167 animatedEntities.getAt(i).actor.act(Gdx.graphics.getDeltaTime()); 168 } 169 len = autoActors.size(); 170 Actor a; 171 for (int i = 0; i < len; i++) { 172 a = autoActors.getAt(i); 173 if(a == null) continue; 174 drawActor(batch, parentAlpha, a); 175 a.act(Gdx.graphics.getDeltaTime()); 176 } 177 } 178 179 /** 180 * Draws one AnimatedEntity, specifically the Actor it contains. Batch must be between start() and end() 181 * @param batch Must have start() called already but not stop() yet during this frame. 182 * @param parentAlpha This can be assumed to be 1.0f if you don't know it 183 * @param ae The AnimatedEntity to draw; the position to draw ae is stored inside it. 184 */ 185 public void drawActor(Batch batch, float parentAlpha, AnimatedEntity ae) 186 { 187 drawActor(batch, parentAlpha, ae.actor); 188 } 189 190 /** 191 * Draws one AnimatedEntity, specifically the Actor it contains. Batch must be between start() and end() 192 * @param batch Must have start() called already but not stop() yet during this frame. 193 * @param parentAlpha This can be assumed to be 1.0f if you don't know it 194 * @param ac The Actor to draw; the position to draw ac is modified and reset based on some fields of this object 195 */ 196 public void drawActor(Batch batch, float parentAlpha, Actor ac) 197 { 198 float prevX = ac.getX(), prevY = ac.getY(); 199 ac.setPosition(prevX - gridOffsetX * cellWidth, prevY + gridOffsetY * cellHeight); 200 ac.draw(batch, parentAlpha); 201 ac.setPosition(prevX, prevY); 202 } 203 204 @Override 205 public void setDefaultForeground(Color defaultForeground) { 206 this.defaultForeground = defaultForeground; 207 } 208 209 @Override 210 public Color getDefaultForegroundColor() { 211 return defaultForeground; 212 } 213 214 public AnimatedEntity getAnimatedEntityByCell(int x, int y) { 215 for(AnimatedEntity ae : animatedEntities) 216 { 217 if(ae.gridX == x && ae.gridY == y) 218 return ae; 219 } 220 return null; 221 } 222 223 /** 224 * Create an AnimatedEntity at position x, y, using the char c in the given color. 225 * @param x 226 * @param y 227 * @param c 228 * @param color 229 * @return 230 */ 231 public AnimatedEntity animateActor(int x, int y, char c, Color color) 232 { 233 return animateActor(x, y, false, c, color); 234 } 235 236 /** 237 * Create an AnimatedEntity at position x, y, using the char c in the given color. If doubleWidth is true, treats 238 * the char c as the left char to be placed in a grid of 2-char cells. 239 * @param x 240 * @param y 241 * @param doubleWidth 242 * @param c 243 * @param color 244 * @return 245 */ 246 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, char c, Color color) 247 { 248 TextureRegion tr = imageMap.get(c); 249 if(tr != null) 250 return animateActor(x, y, doubleWidth, tr, color, String.valueOf(c)); 251 Actor a = textFactory.makeActor(c, color); 252 a.setName(String.valueOf(c)); 253 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 254 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 255 animatedEntities.add(ae); 256 return ae; 257 } 258 259 /** 260 * Create an AnimatedEntity at position x, y, using the String s in the given color. 261 * @param x 262 * @param y 263 * @param s 264 * @param color 265 * @return 266 */ 267 public AnimatedEntity animateActor(int x, int y, String s, Color color) 268 { 269 return animateActor(x, y, false, s, color); 270 } 271 272 /** 273 * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats 274 * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells. 275 * @param x 276 * @param y 277 * @param doubleWidth 278 * @param s 279 * @param color 280 * @return 281 */ 282 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Color color) 283 { 284 Actor a = textFactory.makeActor(s, color); 285 a.setName(s); 286 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 287 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 288 animatedEntities.add(ae); 289 return ae; 290 } 291 /** 292 * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats 293 * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells. 294 * @param x 295 * @param y 296 * @param doubleWidth 297 * @param s 298 * @param colors 299 * @return 300 */ 301 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Collection<Color> colors) 302 { 303 return animateActor(x, y, doubleWidth, s, colors, 2f); 304 } 305 /** 306 * Create an AnimatedEntity at position x, y, using the String s in the given color. If doubleWidth is true, treats 307 * the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells. 308 * @param x 309 * @param y 310 * @param doubleWidth 311 * @param s 312 * @param colors 313 * @param loopTime 314 * @return 315 */ 316 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, String s, Collection<Color> colors, float loopTime) 317 { 318 Actor a = textFactory.makeActor(s, colors, loopTime, doubleWidth); 319 a.setName(s); 320 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 321 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 322 animatedEntities.add(ae); 323 return ae; 324 } 325 /** 326 * Create an AnimatedEntity at position x, y, using the char c in the given colors to cycle through. If doubleWidth 327 * is true, treats the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells. 328 * @param x 329 * @param y 330 * @param doubleWidth 331 * @param c 332 * @param colors 333 * @return 334 */ 335 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, char c, Collection<Color> colors) 336 { 337 return animateActor(x, y, doubleWidth, c, colors, 2f); 338 } 339 /** 340 * Create an AnimatedEntity at position x, y, using the char c in the given colors to cycle through. If doubleWidth 341 * is true, treats the String s as starting in the left cell of a pair to be placed in a grid of 2-char cells. 342 * @param x 343 * @param y 344 * @param doubleWidth 345 * @param c 346 * @param colors 347 * @param loopTime 348 * @return 349 */ 350 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, char c, Collection<Color> colors, float loopTime) 351 { 352 TextureRegion tr = imageMap.get(c); 353 if(tr != null) 354 return animateActor(x, y, doubleWidth, tr, colors, loopTime, String.valueOf(c)); 355 Actor a = textFactory.makeActor(c, colors, loopTime, doubleWidth); 356 a.setName(String.valueOf(c)); 357 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 358 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 359 animatedEntities.add(ae); 360 return ae; 361 } 362 /** 363 * Create an AnimatedEntity at position x, y, using '^' as its contents, but as an image so it can be rotated. 364 * Uses the given colors in a looping pattern, that doesn't count as an animation. If doubleWidth is true, treats 365 * the '^' as starting in the middle of a 2-char cell. 366 * @param x 367 * @param y 368 * @param doubleWidth 369 * @param colors 370 * @param loopTime 371 * @return 372 */ 373 public AnimatedEntity directionMarker(int x, int y, boolean doubleWidth, Collection<Color> colors, float loopTime) 374 { 375 Actor a = textFactory.makeDirectionMarker(colors, loopTime, doubleWidth); 376 a.setName("^"); 377 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 378 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 379 animatedEntities.add(ae); 380 return ae; 381 } 382 public AnimatedEntity directionMarker(int x, int y, boolean doubleWidth, Color color) 383 { 384 Actor a = textFactory.makeDirectionMarker(color); 385 a.setName("^"); 386 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 387 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 388 animatedEntities.add(ae); 389 return ae; 390 } 391 /** 392 * Create an AnimatedEntity at position x, y, using the char c with a color looked up by index in palette. 393 * @param x 394 * @param y 395 * @param c 396 * @param index 397 * @param palette 398 * @return 399 */ 400 public AnimatedEntity animateActor(int x, int y, char c, int index, ArrayList<Color> palette) 401 { 402 return animateActor(x, y, c, palette.get(index)); 403 } 404 405 /** 406 * Create an AnimatedEntity at position x, y, using the String s with a color looked up by index in palette. 407 * @param x 408 * @param y 409 * @param s 410 * @param index 411 * @param palette 412 * @return 413 */ 414 public AnimatedEntity animateActor(int x, int y, String s, int index, ArrayList<Color> palette) 415 { 416 return animateActor(x, y, s, palette.get(index)); 417 } 418 419 /** 420 * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which will be 421 * stretched to fit one cell. 422 * @param x 423 * @param y 424 * @param texture 425 * @return 426 */ 427 public AnimatedEntity animateActor(int x, int y, TextureRegion texture) 428 { 429 return animateActor(x, y, false, texture, Color.WHITE); 430 /* 431 Actor a = textFactory.makeActor(texture, Color.WHITE); 432 a.setName(""); 433 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 434 435 AnimatedEntity ae = new AnimatedEntity(a, x, y); 436 animatedEntities.add(ae); 437 return ae; 438 */ 439 } 440 441 /** 442 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 443 * stretched to fit one cell. 444 * @param x 445 * @param y 446 * @param texture 447 * @param color 448 * @return 449 */ 450 public AnimatedEntity animateActor(int x, int y, TextureRegion texture, Color color) 451 { 452 return animateActor(x, y, false, texture, color); 453 /* 454 Actor a = textFactory.makeActor(texture, color); 455 a.setName(""); 456 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 457 458 AnimatedEntity ae = new AnimatedEntity(a, x, y); 459 animatedEntities.add(ae); 460 return ae; 461 */ 462 } 463 464 /** 465 * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which will be 466 * stretched to fit one cell, or two cells if doubleWidth is true. 467 * @param x 468 * @param y 469 * @param doubleWidth 470 * @param texture 471 * @return 472 */ 473 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture) 474 { 475 return animateActor(x, y, doubleWidth, texture, Color.WHITE); 476 /* 477 Actor a = textFactory.makeActor(texture, Color.WHITE, (doubleWidth ? 2 : 1) * cellWidth, cellHeight); 478 479 a.setName(""); 480 if(doubleWidth) 481 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 482 else 483 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 484 485 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 486 animatedEntities.add(ae); 487 return ae; 488 */ 489 } 490 491 /** 492 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 493 * stretched to fit one cell, or two cells if doubleWidth is true. 494 * @param x 495 * @param y 496 * @param doubleWidth 497 * @param texture 498 * @param color 499 * @return 500 */ 501 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Color color){ 502 return animateActor(x, y, doubleWidth, texture, color, ""); 503 } 504 505 /** 506 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 507 * stretched to fit one cell, or two cells if doubleWidth is true. 508 * @param x 509 * @param y 510 * @param doubleWidth 511 * @param texture 512 * @param color 513 * @return 514 */ 515 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Color color, String name){ 516 Actor a = textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight); 517 a.setName(name); 518 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 519 /* 520 if (doubleWidth) 521 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 522 else 523 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 524 */ 525 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 526 animatedEntities.add(ae); 527 return ae; 528 } 529 /** 530 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 531 * stretched to fit one cell, or two cells if doubleWidth is true. 532 * @param x 533 * @param y 534 * @param doubleWidth 535 * @param texture 536 * @param colors 537 * @return 538 */ 539 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Collection<Color> colors){ 540 return animateActor(x, y, doubleWidth, texture, colors, 2f); 541 } 542 /** 543 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 544 * stretched to fit one cell, or two cells if doubleWidth is true. 545 * @param x 546 * @param y 547 * @param doubleWidth 548 * @param texture 549 * @param colors 550 * @return 551 */ 552 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Collection<Color> colors, float loopTime) { 553 return animateActor(x, y, doubleWidth, texture, colors, loopTime, ""); 554 } 555 /** 556 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which will be 557 * stretched to fit one cell, or two cells if doubleWidth is true. 558 * @param x 559 * @param y 560 * @param doubleWidth 561 * @param texture 562 * @param colors 563 * @return 564 */ 565 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, TextureRegion texture, Collection<Color> colors, float loopTime, String name) { 566 Actor a = textFactory.makeActor(texture, colors, loopTime, doubleWidth, (doubleWidth ? 2 : 1) * cellWidth, cellHeight); 567 a.setName(name); 568 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 569 /* 570 if (doubleWidth) 571 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 572 else 573 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 574 */ 575 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 576 animatedEntities.add(ae); 577 return ae; 578 } 579 580 /** 581 * Create an AnimatedEntity at position x, y, using a TextureRegion with no color modifications, which, if and only 582 * if stretch is true, will be stretched to fit one cell, or two cells if doubleWidth is true. If stretch is false, 583 * this will preserve the existing size of texture. 584 * @param x 585 * @param y 586 * @param doubleWidth 587 * @param stretch 588 * @param texture 589 * @return 590 */ 591 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, boolean stretch, TextureRegion texture) 592 { 593 Actor a = (stretch) 594 ? textFactory.makeActor(texture, Color.WHITE, (doubleWidth ? 2 : 1) * cellWidth, cellHeight) 595 : textFactory.makeActor(texture, Color.WHITE, texture.getRegionWidth(), texture.getRegionHeight()); 596 a.setName(""); 597 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 598 /* 599 if(doubleWidth) 600 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 601 else 602 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 603 */ 604 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 605 animatedEntities.add(ae); 606 return ae; 607 } 608 609 /** 610 * Create an AnimatedEntity at position x, y, using a TextureRegion with the given color, which, if and only 611 * if stretch is true, will be stretched to fit one cell, or two cells if doubleWidth is true. If stretch is false, 612 * this will preserve the existing size of texture. 613 * @param x 614 * @param y 615 * @param doubleWidth 616 * @param stretch 617 * @param texture 618 * @param color 619 * @return 620 */ 621 public AnimatedEntity animateActor(int x, int y, boolean doubleWidth, boolean stretch, TextureRegion texture, Color color) { 622 623 Actor a = (stretch) 624 ? textFactory.makeActor(texture, color, (doubleWidth ? 2 : 1) * cellWidth, cellHeight) 625 : textFactory.makeActor(texture, color, texture.getRegionWidth(), texture.getRegionHeight()); 626 a.setName(""); 627 a.setPosition(adjustX(x, doubleWidth), adjustY(y)); 628 /* 629 if (doubleWidth) 630 a.setPosition(x * 2 * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 631 else 632 a.setPosition(x * cellWidth + getX(), (gridHeight - y - 1) * cellHeight - textFactory.getDescent() + getY()); 633 */ 634 AnimatedEntity ae = new AnimatedEntity(a, x, y, doubleWidth); 635 animatedEntities.add(ae); 636 return ae; 637 } 638 639 /** 640 * Use this method if you use your own {@link IColorCenter} and want this 641 * panel not to allocate its own colors (or fill 642 * {@link DefaultResources#getSCC()} but instead to the provided center. 643 * 644 * @param scc 645 * The color center to use. Should not be {@code null}. 646 * @throws NullPointerException 647 * If {@code scc} is {@code null}. 648 */ 649 public void setColorCenter(IColorCenter<Color> scc) { 650 super.setColorCenter(scc); 651 } 652 653 /** 654 * Gets a "snapshot" of the data represented by this ImageSquidPanel; stores the dimensions, the char data, and the 655 * color data in a way that can be set back to a SquidPanel or ImageSquidPanel using 656 * {@link #setFromSnapshot(String, int, int, int, int)} or its overload that takes a StringBuilder. The actual 657 * contents of the returned StringBuilder are unlikely to be legible in any way if read as text, and are meant to be 658 * concise and stable across versions. 659 * <br> 660 * NOTE: For this version, the mapping of chars to images is not stored in the snapshot, allowing alternate mappings 661 * to be used, such as while graphics are being updated frequently. This also allows the snapshot to be read in from 662 * both normal SquidPanels and ImageSquidPanels. 663 * @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 664 */ 665 public StringBuilder getSnapshot() 666 { 667 return getSnapshot(0, 0, gridWidth, gridHeight); 668 } 669 /** 670 * Gets a "snapshot" of the data represented by this ImageSquidPanel; stores the dimensions, the char data, and the 671 * color data in a way that can be set back to a SquidPanel or ImageSquidPanel using 672 * {@link #setFromSnapshot(String, int, int, int, int)} or its overload that takes a StringBuilder. The actual 673 * contents of the returned StringBuilder are unlikely to be legible in any way if read as text, and are meant to be 674 * concise and stable across versions. This overload allows the first x and y position used to be specified, as well 675 * as the width and height to use (the actual width and height stored may be different if this SquidPanel's 676 * gridWidth and/or gridHeight are smaller than the width and/or height given). 677 * <br> 678 * NOTE: For this version, the mapping of chars to images is not stored in the snapshot, allowing alternate mappings 679 * to be used, such as while graphics are being updated frequently. This also allows the snapshot to be read in from 680 * both normal SquidPanels and ImageSquidPanels. 681 * @param startX the first x position to use in the snapshot, inclusive 682 * @param startY the first y position to use in the snapshot, inclusive 683 * @param width how wide the snapshot area should be; x positions from startX to startX + width - 1 will be used 684 * @param height how tall the snapshot area should be; y positions from startY to startY + height - 1 will be used 685 * @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 686 */ 687 public StringBuilder getSnapshot(int startX, int startY, int width, int height) { 688 width = Math.min(gridWidth - startX, width); 689 height = Math.min(gridHeight - startY, height); 690 StringBuilder sb = new StringBuilder(width * height * 9 + 12); 691 sb.append(width).append('x').append(height).append(':'); 692 for (int x = startX, i = 0; i < width; x++, i++) { 693 sb.append(contents[x], startY, height); 694 } 695 char[] reuse = new char[8]; 696 for (int x = startX, i = 0; i < width; x++, i++) { 697 for (int y = startY, j = 0; j < height; y++, j++) { 698 sb.append(SColor.floatToChars(reuse, colors[x][y])); 699 } 700 } 701 return sb; 702 } 703 704 /** 705 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 706 * ImageSquidPanel from 0,0 (inclusive) up to the dimensions stored in the snapshot to match the snapshot's data. 707 * @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)} 708 * @return this after setting, for chaining 709 */ 710 public ImageSquidPanel setFromSnapshot(StringBuilder snapshot) 711 { 712 return setFromSnapshot(snapshot, 0, 0, -1, -1); 713 } 714 /** 715 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 716 * ImageSquidPanel from the position given by putX,putY (inclusive) up to the dimensions stored in the snapshot 717 * (considering putX and putY as offsets) so they have the values stored in the snapshot. 718 * @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)} 719 * @param putX where to start placing the data from the snapshot, x position 720 * @param putY where to start placing the data from the snapshot, y position 721 * @return this after setting, for chaining 722 */ 723 public ImageSquidPanel setFromSnapshot(StringBuilder snapshot, int putX, int putY) 724 { 725 return setFromSnapshot(snapshot, putX, putY, -1, -1); 726 } 727 /** 728 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 729 * ImageSquidPanel from the position given by putX,putY (inclusive) to putX+limitWidth,putY+limitHeight (exclusive) 730 * so they have the values stored in the snapshot. If limitWidth or limitHeight is negative, this uses the full 731 * width and height of the snapshot (stopping early if it would extend past the gridWidth or gridHeight of this 732 * ImageSquidPanel). 733 * @param snapshot a StringBuilder in a special format as produced by {@link #getSnapshot(int, int, int, int)} 734 * @param putX where to start placing the data from the snapshot, x position 735 * @param putY where to start placing the data from the snapshot, y position 736 * @param limitWidth if negative, uses all of snapshot's width as possible, otherwise restricts the width allowed 737 * @param limitHeight if negative, uses all of snapshot's height as possible, otherwise restricts the height allowed 738 * @return this after setting, for chaining 739 */ 740 public ImageSquidPanel setFromSnapshot(StringBuilder snapshot, int putX, int putY, int limitWidth, int limitHeight) 741 { 742 if(putX >= gridWidth || putY >= gridHeight || snapshot == null || snapshot.length() < 4) return this; 743 if(putX < 0) putX = 0; 744 if(putY < 0) putY = 0; 745 int start = snapshot.indexOf(":")+1, width = StringKit.intFromDec(snapshot), 746 height = StringKit.intFromDec(snapshot, snapshot.indexOf("x") + 1, start), 747 run = start; 748 if(limitWidth < 0) 749 limitWidth = Math.min(width, gridWidth - putX); 750 else 751 limitWidth = Math.min(limitWidth, Math.min(width, gridWidth - putX)); 752 753 if(limitHeight < 0) 754 limitHeight = Math.min(height, gridHeight - putY); 755 else 756 limitHeight = Math.min(limitHeight, Math.min(height, gridHeight - putY)); 757 for (int x = putX, i = 0; i < limitWidth; x++, i++, run += height) { 758 snapshot.getChars(run, run + limitHeight, contents[x], putY); 759 } 760 run = start + width * height; 761 for (int x = putX, i = 0; i < limitWidth; x++, i++) { 762 for (int y = putY, j = 0; j < limitHeight; y++, j++) { 763 colors[x][y] = SColor.charsToFloat(snapshot, run); 764 run += 8; 765 } 766 } 767 return this; 768 } 769 770 /** 771 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 772 * ImageSquidPanel from 0,0 (inclusive) up to the dimensions stored in the snapshot to match the snapshot's data. 773 * <br> 774 * This overload takes a String instead of a StringBuilder for potentially-easier loading from files. 775 * @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)} 776 * @return this after setting, for chaining 777 */ 778 public ImageSquidPanel setFromSnapshot(String snapshot) 779 { 780 return setFromSnapshot(snapshot, 0, 0, -1, -1); 781 } 782 /** 783 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 784 * ImageSquidPanel from the position given by putX,putY (inclusive) up to the dimensions stored in the snapshot 785 * (considering putX and putY as offsets) so they have the values stored in the snapshot. 786 * <br> 787 * This overload takes a String instead of a StringBuilder for potentially-easier loading from files. 788 * @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)} 789 * @param putX where to start placing the data from the snapshot, x position 790 * @param putY where to start placing the data from the snapshot, y position 791 * @return this after setting, for chaining 792 */ 793 public ImageSquidPanel setFromSnapshot(String snapshot, int putX, int putY) 794 { 795 return setFromSnapshot(snapshot, putX, putY, -1, -1); 796 } 797 798 /** 799 * Given a "snapshot" from {@link #getSnapshot(int, int, int, int)}, this assigns the chars and colors in this 800 * ImageSquidPanel from the position given by putX,putY (inclusive) to putX+limitWidth,putY+limitHeight (exclusive) 801 * so they have the values stored in the snapshot. If limitWidth or limitHeight is negative, this uses the full 802 * width and height of the snapshot (stopping early if it would extend past the gridWidth or gridHeight of this 803 * ImageSquidPanel). 804 * <br> 805 * This overload takes a String instead of a StringBuilder for potentially-easier loading from files. 806 * @param snapshot a String in a special format as produced by {@link #getSnapshot(int, int, int, int)} 807 * @param putX where to start placing the data from the snapshot, x position 808 * @param putY where to start placing the data from the snapshot, y position 809 * @param limitWidth if negative, uses all of snapshot's width as possible, otherwise restricts the width allowed 810 * @param limitHeight if negative, uses all of snapshot's height as possible, otherwise restricts the height allowed 811 * @return this after setting, for chaining 812 */ 813 814 public ImageSquidPanel setFromSnapshot(String snapshot, int putX, int putY, int limitWidth, int limitHeight) 815 { 816 if(putX >= gridWidth || putY >= gridHeight || snapshot == null || snapshot.length() < 4) return this; 817 if(putX < 0) putX = 0; 818 if(putY < 0) putY = 0; 819 int start = snapshot.indexOf(":")+1, width = StringKit.intFromDec(snapshot), 820 height = StringKit.intFromDec(snapshot, snapshot.indexOf("x") + 1, start), 821 run = start; 822 if(limitWidth < 0) 823 limitWidth = Math.min(width, gridWidth - putX); 824 else 825 limitWidth = Math.min(limitWidth, Math.min(width, gridWidth - putX)); 826 827 if(limitHeight < 0) 828 limitHeight = Math.min(height, gridHeight - putY); 829 else 830 limitHeight = Math.min(limitHeight, Math.min(height, gridHeight - putY)); 831 for (int x = putX, i = 0; i < limitWidth; x++, i++, run += height) { 832 snapshot.getChars(run, run + limitHeight, contents[x], putY); 833 } 834 run = start + width * height; 835 for (int x = putX, i = 0; i < limitWidth; x++, i++) { 836 for (int y = putY, j = 0; j < limitHeight; y++, j++) { 837 colors[x][y] = SColor.charsToFloat(snapshot, run); 838 run += 8; 839 } 840 } 841 return this; 842 } 843 844 /** 845 * Makes it so when the char swapOut would be drawn, the TextureRegion swapIn is drawn instead. 846 * This will apply for as long as this ImageSquidPanel is in use unless swapIn is removed with 847 * {@link #removeImageSwap(char)} or directly changing this object's {@link #imageMap}. 848 * @param swapOut the char to avoid rendering and replace with an image 849 * @param swapIn the image to replace the character with 850 */ 851 public void setImageSwap(final char swapOut, final TextureRegion swapIn) 852 { 853 if(swapIn != null) 854 imageMap.put(swapOut, swapIn); 855 } 856 857 /** 858 * Removes the char toRemove from the mapping of chars to replace with images, or does nothing if this did not 859 * already replace toRemove with an image. This means toRemove will render as itself, if present in the font. 860 * @param toRemove the char to render as a glyph instead of a texture 861 */ 862 public void removeImageSwap(final char toRemove) 863 { 864 imageMap.remove(toRemove); 865 } 866 867 /** 868 * If there is a TextureRegion that would replace the char toFind when drawn, this will return that TextureRegion, 869 * otherwise it returns null. 870 * @param toFind the char to find the corresponding TextureRegion that it could be mapped to 871 * @return the corresponding TextureRegion if it exists, or null otherwise 872 */ 873 public TextureRegion getImageSwap(final char toFind) 874 { 875 return imageMap.get(toFind); 876 } 877 878 /** 879 * Meant for taking easy-to-write chars and generating chars that can map to images, while still allowing the 880 * original easy-to-write char to be used as its own non-image char. Given a char that should be from the 881 * "relatively common" parts of Unicode (essentially, anything with a codepoint before 0x1000, or with a small risk 882 * of collision, anything before 0x8000), this returns a character from the "Private Use Area" of Unicode that will 883 * probably be unique for most kinds of input. 4096 possible chars can be returned by this method. 884 * 885 * As an example, you may want to map the char 'g' to an image of a goblin, but still sometimes show the actual 'g' 886 * for some other monster or a variant on goblins you don't have an image for. You could call 887 * {@code char goblinChar = ImageSquidPanel.getUnusedChar('g');} and then register goblinChar in the image mapping 888 * with {@code myImageSquidPanel.setImageSwap(goblinChar, goblinImage);}, which would allow you to draw 'g' directly 889 * by placing a 'g' with put(), or draw goblinImage by placing either goblinChar or 890 * {@code ImageSquidPanel.getUnusedChar('g')} with put(). 891 * 892 * @param initial a char that ideally should be from the earlier parts of Unicode (before 0x1000 is ideal) 893 * @return a char from Unicode's private use area that is unlikely to be found otherwise or accidentally 894 */ 895 public static char getUnusedChar(final char initial) 896 { 897 return (char)(((initial & 0x7000) >>> 3 ^ initial) & 0x0FFF | 0xE000); 898 } 899}