stb_tilemap_editor.h 142.4 KB
Newer Older
S
Sean Barrett 已提交
1
// stb_tilemap_editor.h - v0.41 - Sean Barrett - http://nothings.org/stb
2
// placed in the public domain - not copyrighted - first released 2014-09
S
Sean Barrett 已提交
3 4 5 6
//
// Embeddable tilemap editor for C/C++
//
//
S
Sean Barrett 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
// TABLE OF CONTENTS
//    FAQ
//    How to compile/use the library
//    Additional configuration macros
//    API documentation
//    Info on editing multiple levels
//    Revision history
//    Todo
//    Credits
//    License
//
//
// FAQ
//
//   Q: What counts as a tilemap for this library?
//
//   A: An array of rectangles, where each rectangle contains a small
//      stack of images.
//
//   Q: What are the limitations?
//
//   A: Maps are limited to 4096x4096 in dimension.
//      Each map square can only contain a stack of at most 32 images.
//      A map can only use up to 32768 distinct image tiles.
//
//   Q: How do I compile this?
//
//   A: You need to #define several symbols before #including it, but only
//      in one file. This will cause all the function definitions to be
//      generated in that file. See the "HOW TO COMPILE" section.
//
//   Q: What advantages does this have over a standalone editor?
//
//   A: For one, you can integrate the editor into your game so you can
//      flip between editing and testing without even switching windows.
//      For another, you don't need an XML parser to get at the map data.
//
//   Q: Can I live-edit my game maps?
//
//   A: Not really, the editor keeps its own map representation.
//
//   Q: How do I save and load maps?
//
//   A: You have to do this yourself. The editor provides serialization
//      functions (get & set) for reading and writing the map it holds.
//      You can choose whatever format you want to store the map to on
//      disk; you just need to provide functions to convert. (For example,
//      I actually store the editor's map representation to disk basically
//      as-is; then I have a single function that converts from the editor
//      map representation to the game representation, which is used both
//      to go from editor-to-game and from loaded-map-to-game.)
//
//   Q: I want to have tiles change appearance based on what's
//      adjacent, or other tile-display/substitution trickiness.
//
//   A: You can do this when you convert from the editor's map
//      representation to the game representation, but there's
//      no way to show this live in the editor.
//
S
Sean Barrett 已提交
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
//   Q: The editor appears to be put map location (0,0) at the top left?
//      I want to use a different coordinate system in my game (e.g. y
//      increasing upwards, or origin at the center).
//
//   A: You can do this when you convert from the editor's map
//      representation to the game representation. (Don't forget to
//      translate link coordinates as well!)
//
//   Q: The editor appears to put pixel (0,0) at the top left? I want
//      to use a different coordinate system in my game.
//
//   A: The editor defines an "editor pixel coordinate system" with
//      (0,0) at the top left and requires you to display things in
//      that coordinate system. You can freely remap those coordinates
//      to anything you want on screen.
//
S
Sean Barrett 已提交
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
//   Q: How do I scale the user interface?
//
//   A: Since you do all the rendering, you can scale up all the rendering
//      calls that the library makes to you. If you do, (a) you need
//      to also scale up the mouse coordinates, and (b) you may want
//      to scale the map display back down so that you're only scaling
//      the UI and not everything. See the next question.
//
//   Q: How do I scale the map display?
//
//   A: Use stbte_set_spacing() to change the size that the map is displayed
//      at. Note that the "callbacks" to draw tiles are used for both drawing
//      the map and drawing the tile palette, so that callback may need to
//      draw at two different scales. You should choose the scales to match
//       You can tell them apart because the
//      tile palette gets NULL for the property pointer.
//
//   Q: How does object editing work?
//
//   A: One way to think of this is that in the editor, you're placing
//      spawners, not objects. Each spawner must be tile-aligned, because
//      it's only a tile editor. Each tile (stack of layers) gets
//      an associated set of properties, and it's up to you to
//      determine what properties should appear for a given tile,
//      based on e.g. the spawners that are in it.
//
//   Q: How are properties themselves handled?
//
//   A: All properties, regardless of UI behavior, are internally floats.
//      Each tile has an array of floats associated with it, which is
//      passed back to you when drawing the tiles so you can draw
//      objects appropriately modified by the properties.
S
Sean Barrett 已提交
114
//
S
Sean Barrett 已提交
115 116
//   Q: What if I want to have two different objects/spawners in
//      one tile, both of which have their own properties?
S
Sean Barrett 已提交
117
//
S
Sean Barrett 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
//   A: Make sure STBTE_MAX_PROPERTIES is large enough for the sum of
//      properties in both objects, and then you have to explicitly
//      map the property slot #s to the appropriate objects. They'll
//      still all appear in a single property panel; there's no way
//      to get multiple panels.
//
//   Q: Can I do one-to-many linking?
//
//   A: The library only supports one link per tile. However, you
//      can have multiple tiles all link to a single tile. So, you
//      can fake one-to-many linking by linking in the reverse
//      direction.
//
//   Q: What if I have two objects in the same tile, and they each
//      need an independent link? Or I have two kinds of link associated
//      with a single object?
//
//   A: There is no way to do this. (Unless you can reverse one link.)
//
//   Q: How does cut & paste interact with object properties & links?
//
//   A: Currently the library has no idea which properties or links
//      are associated with which layers of a tile. So currently, the
//      library will only copy properties & links if the layer panel
//      is set to allow all layers to be copied, OR if you set the
//      "props" in the layer panel to "always". Similarly, you can
//      set "props" to "none" so it will never copy.
//
//   Q: What happens if the library gets a memory allocation failure
//      while I'm editing? Will I lose my work?
//
//   A: The library allocates all editor memory when you create
S
Sean Barrett 已提交
150
//      the tilemap. It allocates a maximally-sized map and a
S
Sean Barrett 已提交
151 152 153 154 155 156 157 158 159 160 161
//      fixed-size undo buffer (and the fixed-size copy buffer
//      is static), and never allocates memory while it's running.
//      So it can't fail due to running out of memory.
//
//   Q: What happens if the library crashes while I'm editing? Will
//      I lose my work?
//
//   A: Yes. Save often.
//
//
// HOW TO COMPILE
S
Sean Barrett 已提交
162 163 164
//
//   This header file contains both the header file and the
//   implementation file in one. To create the implementation,
S
Sean Barrett 已提交
165
//   in one source file define a few symbols first and then
S
Sean Barrett 已提交
166 167 168 169 170
//   include this header:
//
//      #define STB_TILEMAP_EDITOR_IMPLEMENTATION
//      // this triggers the implementation
//
S
Sherjil Ozair 已提交
171
//      void STBTE_DRAW_RECT(int x0, int y0, int x1, int y1, unsigned int color);
S
Sean Barrett 已提交
172 173
//      // this must draw a filled rectangle (exclusive on right/bottom)
//      // color = (r<<16)|(g<<8)|(b)
S
Sean Barrett 已提交
174
//
S
Sean Barrett 已提交
175
//      void STBTE_DRAW_TILE(int x0, int y0,
S
Sean Barrett 已提交
176
//                    unsigned short id, int highlight, float *data);
S
Sean Barrett 已提交
177
//      // this draws the tile image identified by 'id' in one of several
S
Sean Barrett 已提交
178
//      // highlight modes (see STBTE_drawmode_* in the header section);
S
Sean Barrett 已提交
179
//      // if 'data' is NULL, it's drawing the tile in the palette; if 'data'
S
Sean Barrett 已提交
180 181
//      // is not NULL, it's drawing a tile on the map, and that is the data
//      // associated with that map tile
S
Sean Barrett 已提交
182 183 184 185
//
//      #include "stb_tilemap_editor.h"
//
//   Optionally you can define the following functions before the include;
S
Sean Barrett 已提交
186
//   note these must be macros (but they can just call a function) so
S
Sean Barrett 已提交
187 188 189 190 191 192 193 194 195 196 197 198
//   this library can #ifdef to detect if you've defined them:
//
//      #define STBTE_PROP_TYPE(int n, short *tiledata, float *params) ...
//      // Returns the type of the n'th property of a given tile, which
//      // controls how it is edited. Legal types are:
//      //     0                    /* no editable property in this slot */
//      //     STBTE_PROP_int       /* uses a slider to adjust value     */
//      //     STBTE_PROP_float     /* uses a weird multi-axis control   */
//      //     STBTE_PROP_bool      /* uses a checkbox to change value   */
//      // And you can bitwise-OR in the following flags:
//      //     STBTE_PROP_disabled
//      // Note that all of these are stored as floats in the param array.
S
Sean Barrett 已提交
199 200 201
//      // The integer slider is limited in precision based on the space
//      // available on screen, so for wide-ranged integers you may want
//      // to use floats instead.
S
Sean Barrett 已提交
202 203 204 205 206 207 208
//      //
//      // Since the tiledata is passed to you, you can choose which property
//      // is bound to that slot based on that data.
//      //
//      // Changing the type of a parameter does not cause the underlying
//      // value to be clamped to the type min/max except when the tile is
//      // explicitly selected.
S
Sean Barrett 已提交
209
//
S
Sean Barrett 已提交
210 211 212 213 214 215 216 217 218
//      #define STBTE_PROP_NAME(int n, short *tiledata, float *params) ...
//      // these return a string with the name for slot #n in the float
//      // property list for the tile.
//
//      #define STBTE_PROP_MIN(int n, short *tiledata) ...your code here...
//      #define STBTE_PROP_MAX(int n, short *tiledata) ...your code here...
//      // These return the allowable range for the property values for
//      // the specified slot. It is never called for boolean types.
//
S
Sean Barrett 已提交
219 220 221 222 223 224 225 226 227
//      #define STBTE_PROP_FLOAT_SCALE(int n, short *tiledata, float *params)
//      // This rescales the float control for a given property; by default
//      // left mouse drags add integers, right mouse drags adds fractions,
//      // but you can rescale this per-property.
//
//      #define STBTE_FLOAT_CONTROL_GRANULARITY       ... value ...
//      // This returns the number of pixels of mouse motion necessary
//      // to advance the object float control. Default is 4
//
S
Sean Barrett 已提交
228 229 230 231 232
//      #define STBTE_ALLOW_LINK(short *src, float *src_data,  \
//                               short *dest, float *dest_data) ...your code...
//      // this returns true or false depending on whether you allow a link
//      // to be drawn from a tile 'src' to a tile 'dest'. if you don't
//      // define this, linking will not be supported
S
Sean Barrett 已提交
233
//
S
Sean Barrett 已提交
234 235 236 237 238 239 240 241
//      #define STBTE_LINK_COLOR(short *src, float *src_data,  \
//                               short *dest, float *dest_data) ...your code...
//      // return a color encoded as a 24-bit unsigned integer in the
//      // form 0xRRGGBB. If you don't define this, default colors will
//      // be used.
//
//
//      [[ support for those below is not implemented yet ]]
S
Sean Barrett 已提交
242 243 244 245 246 247 248 249 250 251 252 253
//
//      #define STBTE_HITTEST_TILE(x0,y0,id,mx,my)   ...your code here...
//      // this returns true or false depending on whether the mouse
//      // pointer at mx,my is over (touching) a tile of type 'id'
//      // displayed at x0,y0. Normally stb_tilemap_editor just does
//      // this hittest based on the tile geometry, but if you have
//      // tiles whose images extend out of the tile, you'll need this.
//
// ADDITIONAL CONFIGURATION
//
//   The following symbols set static limits which determine how much
//   memory will be allocated for the editor. You can override them
L
luz.paz 已提交
254
//   by making similar definitions, but memory usage will increase.
S
Sean Barrett 已提交
255 256 257 258 259
//
//      #define STBTE_MAX_TILEMAP_X      200   // max 4096
//      #define STBTE_MAX_TILEMAP_Y      200   // max 4096
//      #define STBTE_MAX_LAYERS         8     // max 32
//      #define STBTE_MAX_CATEGORIES     100
S
Sean Barrett 已提交
260
//      #define STBTE_UNDO_BUFFER_BYTES  (1 << 24) // 16 MB
S
Sean Barrett 已提交
261
//      #define STBTE_MAX_COPY           90000  // e.g. 300x300
262
//      #define STBTE_MAX_PROPERTIES     10     // max properties per tile
S
Sean Barrett 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276
//
// API
//
//   Further documentation appears in the header-file section below.
//
// EDITING MULTIPLE LEVELS
//
//   You can only have one active editor instance. To switch between multiple
//   levels, you can either store the levels in your own format and copy them
//   in and out of the editor format, or you can create multiple stbte_tilemap
//   objects and switch between them. The latter has the advantage that each
//   stbte_tilemap keeps its own undo state. (The clipboard is global, so
//   either approach allows cut&pasting between levels.)
//
S
Sean Barrett 已提交
277
// REVISION HISTORY
S
Sean Barrett 已提交
278
//   0.41  fix warnings
S
Sean Barrett 已提交
279 280
//   0.40  fix warning
//   0.39  fix warning
S
Sean Barrett 已提交
281
//   0.38  fix warning
282
//   0.37  fix warning
S
Sean Barrett 已提交
283
//   0.36  minor compiler support
S
Sean Barrett 已提交
284
//   0.35  layername button changes
285 286
//          - layername buttons grow with the layer panel
//          - fix stbte_create_map being declared as stbte_create
287
//          - fix declaration of stbte_create_map
S
Sean Barrett 已提交
288 289 290 291 292 293 294 295 296 297 298 299
//   0.30  properties release
//          - properties panel for editing user-defined "object" properties
//          - can link each tile to one other tile
//          - keyboard interface
//          - fix eraser tool bug (worked in complex cases, failed in simple)
//          - undo/redo tools have visible disabled state
//          - tiles on higher layers draw on top of adjacent lower-layer tiles
//   0.20  erasable release
//          - eraser tool
//          - fix bug when pasting into protected layer
//          - better color scheme
//          - internal-use color picker
S
Sean Barrett 已提交
300
//   0.10  initial release
S
Sean Barrett 已提交
301
//
S
Sean Barrett 已提交
302 303 304 305 306 307 308 309
// TODO
//
//   Separate scroll state for each category
//   Implement paint bucket
//   Support STBTE_HITTEST_TILE above
//  ?Cancel drags by clicking other button? - may be fixed
//   Finish support for toolbar at side
//
S
Sean Barrett 已提交
310 311
// CREDITS
//
S
Sean Barrett 已提交
312 313 314 315 316 317
//
//   Main editor & features
//      Sean Barrett
//   Additional features:
//      Josh Huelsman
//   Bugfixes:
S
Sean Barrett 已提交
318
//      Ryan Whitworth
319
//      Eugene Opalev
S
Sean Barrett 已提交
320
//
S
Sean Barrett 已提交
321 322
// LICENSE
//
323
//   See end of file for license information.
S
Sean Barrett 已提交
324 325 326 327 328 329 330 331 332 333



///////////////////////////////////////////////////////////////////////
//
//   HEADER SECTION

#ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
#define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H

334
#ifdef _WIN32
E
Eugene Opalev 已提交
335
  #ifndef _CRT_SECURE_NO_WARNINGS
336
  #define _CRT_SECURE_NO_WARNINGS
E
Eugene Opalev 已提交
337
  #endif
338 339 340 341
  #include <stdlib.h>
  #include <stdio.h>
#endif

S
Sean Barrett 已提交
342 343 344 345 346 347 348 349 350 351
typedef struct stbte_tilemap stbte_tilemap;

// these are the drawmodes used in STBTE_DRAW_TILE
enum
{
   STBTE_drawmode_deemphasize = -1,
   STBTE_drawmode_normal      =  0,
   STBTE_drawmode_emphasize   =  1,
};

S
Sean Barrett 已提交
352 353 354 355 356 357 358
// these are the property types
#define STBTE_PROP_none     0
#define STBTE_PROP_int      1
#define STBTE_PROP_float    2
#define STBTE_PROP_bool     3
#define STBTE_PROP_disabled 4

S
Sean Barrett 已提交
359 360 361 362 363
////////
//
// creation
//

364
extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles);
S
Sean Barrett 已提交
365 366 367 368 369 370 371 372 373 374 375 376
// create an editable tilemap
//   map_x      : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X
//   map_y      : dimensions of map vertically (user can change this in editor)    <= STBTE_MAX_TILEMAP_Y
//   map_layers : number of layers to use (fixed), <= STBTE_MAX_LAYERS
//   spacing_x  : initial horizontal distance between left edges of map tiles in stb_tilemap_editor pixels
//   spacing_y  : initial vertical distance between top edges of map tiles in stb_tilemap_editor pixels
//   max_tiles  : maximum number of tiles that can defined
//
// If insufficient memory, returns NULL

extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category);
// call this repeatedly for each tile to install the tile definitions into the editable tilemap
377
//   tm        : tilemap created by stbte_create_map
S
Sean Barrett 已提交
378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394
//   id        : unique identifier for each tile, 0 <= id < 32768
//   layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7
//               (note that onscreen, the editor numbers the layers from 1 not 0)
//               layer 0 is the furthest back, layer 1 is just in front of layer 0, etc
//   category  : which category this tile is grouped in

extern void stbte_set_display(int x0, int y0, int x1, int y1);
// call this once to set the size; if you resize, call it again


/////////
//
// every frame
//

extern void stbte_draw(stbte_tilemap *tm);

S
Sean Barrett 已提交
395
extern void stbte_tick(stbte_tilemap *tm, float time_in_seconds_since_last_frame);
S
Sean Barrett 已提交
396 397 398 399 400 401

////////////
//
//  user input
//

S
Sherjil Ozair 已提交
402
// if you're using SDL, call the next function for SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP, SDL_MOUSEWHEEL;
S
Sean Barrett 已提交
403 404 405 406 407 408 409 410
// the transformation lets you scale from SDL mouse coords to stb_tilemap_editor coords
extern void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xscale, float yscale, int xoffset, int yoffset);

// otherwise, hook these up explicitly:
extern void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey);
extern void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey);
extern void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll);

S
Sherjil Ozair 已提交
411 412
// note: at the moment, mouse wheel events (SDL_MOUSEWHEEL) are ignored.

S
Sean Barrett 已提交
413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
// for keyboard, define your own mapping from keys to the following actions.
// this is totally optional, as all features are accessible with the mouse
enum stbte_action
{
   STBTE_tool_select,
   STBTE_tool_brush,
   STBTE_tool_erase,
   STBTE_tool_rectangle,
   STBTE_tool_eyedropper,
   STBTE_tool_link,
   STBTE_act_toggle_grid,
   STBTE_act_toggle_links,
   STBTE_act_undo,
   STBTE_act_redo,
   STBTE_act_cut,
   STBTE_act_copy,
   STBTE_act_paste,
   STBTE_scroll_left,
   STBTE_scroll_right,
   STBTE_scroll_up,
   STBTE_scroll_down,
};
extern void stbte_action(stbte_tilemap *tm, enum stbte_action act);
S
Sean Barrett 已提交
436 437 438

////////////////
//
S
Sean Barrett 已提交
439
//  save/load
S
Sean Barrett 已提交
440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
//
//  There is no editor file format. You have to save and load the data yourself
//  through the following functions. You can also use these functions to get the
//  data to generate game-formatted levels directly. (But make sure you save
//  first! You may also want to autosave to a temp file periodically, etc etc.)

#define STBTE_EMPTY    -1

extern void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y);
// get the dimensions of the level, since the user can change them

extern short* stbte_get_tile(stbte_tilemap *tm, int x, int y);
// returns an array of shorts that is 'map_layers' in length. each short is
// either one of the tile_id values from define_tile, or STBTE_EMPTY.

S
Sean Barrett 已提交
455
extern float *stbte_get_properties(stbte_tilemap *tm, int x, int y);
S
Sean Barrett 已提交
456 457 458
// get the property array associated with the tile at x,y. this is an
// array of floats that is STBTE_MAX_PROPERTIES in length; you have to
// interpret the slots according to the semantics you've chosen
S
Sean Barrett 已提交
459

S
Sean Barrett 已提交
460 461
extern void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty);
// gets the link associated with the tile at x,y.
S
Sean Barrett 已提交
462

S
Sean Barrett 已提交
463 464 465 466 467 468 469 470 471 472 473
extern void stbte_set_dimensions(stbte_tilemap *tm, int max_x, int max_y);
// set the dimensions of the level, overrides previous stbte_create_map()
// values or anything the user has changed

extern void stbte_clear_map(stbte_tilemap *tm);
// clears the map, including the region outside the defined region, so if the
// user expands the map, they won't see garbage there

extern void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile);
// tile is your tile_id from define_tile, or STBTE_EMPTY

S
Sean Barrett 已提交
474 475 476 477 478 479
extern void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val);
// set the value of the n'th slot of the tile at x,y

extern void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty);
// set a link going from x,y to destx,desty. to force no link,
// use destx=desty=-1
S
Sean Barrett 已提交
480

S
Sean Barrett 已提交
481 482 483 484 485 486 487
////////
//
// optional
//

extern void stbte_set_background_tile(stbte_tilemap *tm, short id);
// selects the tile to fill the bottom layer with and used to clear bottom tiles to;
S
Sean Barrett 已提交
488
// should be same ID as
S
Sean Barrett 已提交
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537

extern void stbte_set_sidewidths(int left, int right);
// call this once to set the left & right side widths. don't call
// it again since the user can change it

extern void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y);
// call this to set the spacing of map tiles and the spacing of palette tiles.
// if you rescale your display, call it again (e.g. you can implement map zooming yourself)

extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername);
// sets a string name for your layer that shows in the layer selector. note that this
// makes the layer selector wider. 'layer' is from 0..(map_layers-1)

#endif

#ifdef STB_TILEMAP_EDITOR_IMPLEMENTATION

#ifndef STBTE_ASSERT
#define STBTE_ASSERT assert
#include <assert.h>
#endif

#ifdef _MSC_VER
#define STBTE__NOTUSED(v)  (void)(v)
#else
#define STBTE__NOTUSED(v)  (void)sizeof(v)
#endif

#ifndef STBTE_MAX_TILEMAP_X
#define STBTE_MAX_TILEMAP_X      200
#endif

#ifndef STBTE_MAX_TILEMAP_Y
#define STBTE_MAX_TILEMAP_Y      200
#endif

#ifndef STBTE_MAX_LAYERS
#define STBTE_MAX_LAYERS         8
#endif

#ifndef STBTE_MAX_CATEGORIES
#define STBTE_MAX_CATEGORIES     100
#endif

#ifndef STBTE_MAX_COPY
#define STBTE_MAX_COPY           65536
#endif

#ifndef STBTE_UNDO_BUFFER_BYTES
S
Sean Barrett 已提交
538
#define STBTE_UNDO_BUFFER_BYTES  (1 << 24) // 16 MB
S
Sean Barrett 已提交
539 540
#endif

S
Sean Barrett 已提交
541 542 543 544 545 546 547 548 549 550 551
#ifndef STBTE_PROP_TYPE
#define STBTE__NO_PROPS
#define STBTE_PROP_TYPE(n,td,tp)   0
#endif

#ifndef STBTE_PROP_NAME
#define STBTE_PROP_NAME(n,td,tp)  ""
#endif

#ifndef STBTE_MAX_PROPERTIES
#define STBTE_MAX_PROPERTIES           10
S
Sean Barrett 已提交
552 553 554 555 556 557 558 559 560 561 562 563 564 565
#endif

#ifndef STBTE_PROP_MIN
#define STBTE_PROP_MIN(n,td,tp)  0
#endif

#ifndef STBTE_PROP_MAX
#define STBTE_PROP_MAX(n,td,tp)  100.0
#endif

#ifndef STBTE_PROP_FLOAT_SCALE
#define STBTE_PROP_FLOAT_SCALE(n,td,tp)  1   // default scale size
#endif

S
Sean Barrett 已提交
566 567 568 569
#ifndef STBTE_FLOAT_CONTROL_GRANULARITY
#define STBTE_FLOAT_CONTROL_GRANULARITY 4
#endif

S
Sean Barrett 已提交
570

S
Sean Barrett 已提交
571 572 573 574 575 576 577 578 579 580 581 582
#define STBTE__UNDO_BUFFER_COUNT  (STBTE_UNDO_BUFFER_BYTES>>1)

#if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096
#error "Maximum editable map size is 4096 x 4096"
#endif
#if STBTE_MAX_LAYERS > 32
#error "Maximum layers allowed is 32"
#endif
#if STBTE_UNDO_BUFFER_COUNT & (STBTE_UNDO_BUFFER_COUNT-1)
#error "Undo buffer size must be a power of 2"
#endif

S
Sean Barrett 已提交
583 584 585 586 587 588 589 590
#if STBTE_MAX_PROPERTIES == 0
#define STBTE__NO_PROPS
#endif

#ifdef STBTE__NO_PROPS
#undef STBTE_MAX_PROPERTIES
#define STBTE_MAX_PROPERTIES 1  // so we can declare arrays
#endif
S
Sean Barrett 已提交
591

S
Sean Barrett 已提交
592 593 594 595 596
typedef struct
{
   short x,y;
} stbte__link;

S
Sean Barrett 已提交
597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
enum
{
   STBTE__base,
   STBTE__outline,
   STBTE__text,

   STBTE__num_color_aspects,
};

enum
{
   STBTE__idle,
   STBTE__over,
   STBTE__down,
   STBTE__over_down,
   STBTE__selected,
   STBTE__selected_over,
   STBTE__disabled,
   STBTE__num_color_states,
};

enum
{
   STBTE__cexpander,
   STBTE__ctoolbar,
   STBTE__ctoolbar_button,
   STBTE__cpanel,
   STBTE__cpanel_sider,
   STBTE__cpanel_sizer,
   STBTE__cscrollbar,
   STBTE__cmapsize,
   STBTE__clayer_button,
   STBTE__clayer_hide,
   STBTE__clayer_lock,
   STBTE__clayer_solo,
   STBTE__ccategory_button,

   STBTE__num_color_modes,
};

#ifdef STBTE__COLORPICKER
static char *stbte__color_names[] =
{
   "expander", "toolbar", "tool button", "panel",
   "panel c1", "panel c2", "scollbar", "map button",
   "layer", "hide", "lock", "solo",
   "category",
};
#endif // STBTE__COLORPICKER

      // idle,    over,     down,    over&down, selected, sel&over, disabled
static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =
{
   {
      { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050, },
      { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060, },
      { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
   }, {
      { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, },
      { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, },
      { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, },
   }, {
      { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040, },
      { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08, },
      { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074, },
   }, {
      { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024, },
      { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030, },
      { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
   }, {
      { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, },
      { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060, },
      { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
   }, {
      { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, },
      { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060, },
      { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
   }, {
      { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040, },
      { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050, },
      { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, },
   }, {
      { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, },
      { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, },
      { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
   }, {
      { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, },
      { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, },
      { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
   }, {
      { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c, },
      { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060, },
      { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090, },
   }, {
      { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050, },
      { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060, },
      { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090, },
   }, {
      { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050, },
      { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060, },
      { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090, },
   }, {
      { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828, },
      { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838, },
      { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090, },
   },
};

#define STBTE_COLOR_TILEMAP_BACKGROUND      0x000000
#define STBTE_COLOR_TILEMAP_BORDER          0x203060
#define STBTE_COLOR_TILEMAP_HIGHLIGHT       0xffffff
#define STBTE_COLOR_GRID                    0x404040
#define STBTE_COLOR_SELECTION_OUTLINE1      0xdfdfdf
#define STBTE_COLOR_SELECTION_OUTLINE2      0x303030
#define STBTE_COLOR_TILEPALETTE_OUTLINE     0xffffff
#define STBTE_COLOR_TILEPALETTE_BACKGROUND  0x000000

S
Sean Barrett 已提交
714 715 716 717 718 719 720 721 722 723 724 725 726
#ifndef STBTE_LINK_COLOR
#define STBTE_LINK_COLOR(src,sp,dest,dp)    0x5030ff
#endif

#ifndef STBTE_LINK_COLOR_DRAWING
#define STBTE_LINK_COLOR_DRAWING            0xff40ff
#endif

#ifndef STBTE_LINK_COLOR_DISALLOWED
#define STBTE_LINK_COLOR_DISALLOWED         0x602060
#endif


S
Sean Barrett 已提交
727 728 729 730 731 732 733 734 735 736 737 738 739
// disabled, selected, down, over
static unsigned char stbte__state_to_index[2][2][2][2] =
{
   {
      { { STBTE__idle    , STBTE__over          }, { STBTE__down    , STBTE__over_down }, },
      { { STBTE__selected, STBTE__selected_over }, { STBTE__down    , STBTE__over_down }, },
   },{
      { { STBTE__disabled, STBTE__disabled      }, { STBTE__disabled, STBTE__disabled  }, },
      { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled  }, },
   }
};
#define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over]
#define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id))
S
Sean Barrett 已提交
740

S
Sean Barrett 已提交
741
#define STBTE__FONT_HEIGHT    9
S
Sean Barrett 已提交
742
static short stbte__font_offset[95+16];
S
Sean Barrett 已提交
743
static short stbte__fontdata[769] =
S
Sean Barrett 已提交
744
{
S
Sean Barrett 已提交
745
   4,9,6,9,9,9,9,8,9,8,4,9,7,7,7,7,4,2,6,8,6,6,7,3,4,4,8,6,3,6,2,6,6,6,6,6,6,
S
Sean Barrett 已提交
746 747
   6,6,6,6,6,2,3,5,4,5,6,6,6,6,6,6,6,6,6,6,6,6,7,6,7,7,7,6,7,6,6,6,6,7,7,6,6,
   6,4,6,4,7,7,3,6,6,5,6,6,5,6,6,4,5,6,4,7,6,6,6,6,6,6,6,6,6,7,6,6,6,5,2,5,8,
S
Sean Barrett 已提交
748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
   0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146,
   511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16,
   124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496,
   496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257,
   257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8,
   24,56,120,56,24,8,32,48,56,60,56,48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127,
   127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93,
   55,114,80,4,7,3,62,127,99,65,65,99,127,62,8,42,62,28,28,62,42,8,8,8,62,62,
   8,8,128,224,96,8,8,8,8,8,8,96,96,96,48,24,12,6,3,62,127,89,77,127,62,64,66,
   127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39,
   103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54,
   6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28,
   8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127,
   54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9,
   9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64,
   64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127,
   127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65,
   81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127,
   64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119,
   99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65,
   65,127,127,8,12,6,3,6,12,8,64,64,64,64,64,64,64,3,7,4,32,116,84,84,124,120,
   127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92,
   24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122,
   64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28,
   124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124,
   68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124,
   64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108,
   68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65,
   65,119,62,8,16,24,12,12,24,24,12,4,
S
Sean Barrett 已提交
777
};
S
Sean Barrett 已提交
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795

typedef struct
{
   short id;
   unsigned short category_id;
   char *category;
   unsigned int layermask;
} stbte__tileinfo;

#define MAX_LAYERMASK    (1 << (8*sizeof(unsigned int)))

typedef short stbte__tiledata;

#define STBTE__NO_TILE   -1

enum
{
   STBTE__panel_toolbar,
S
Sean Barrett 已提交
796
   STBTE__panel_colorpick,
S
Sean Barrett 已提交
797 798
   STBTE__panel_info,
   STBTE__panel_layers,
S
Sean Barrett 已提交
799
   STBTE__panel_props,
S
Sean Barrett 已提交
800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817
   STBTE__panel_categories,
   STBTE__panel_tiles,

   STBTE__num_panel,
};

enum
{
   STBTE__side_left,
   STBTE__side_right,
   STBTE__side_top,
   STBTE__side_bottom,
};

enum
{
   STBTE__tool_select,
   STBTE__tool_brush,
818
   STBTE__tool_erase,
S
Sean Barrett 已提交
819 820 821
   STBTE__tool_rect,
   STBTE__tool_eyedrop,
   STBTE__tool_fill,
S
Sean Barrett 已提交
822 823 824 825
   STBTE__tool_link,

   STBTE__tool_showgrid,
   STBTE__tool_showlinks,
S
Sean Barrett 已提交
826 827 828 829 830 831 832 833 834

   STBTE__tool_undo,
   STBTE__tool_redo,
   // copy/cut/paste aren't included here because they're displayed differently

   STBTE__num_tool,
};

// icons are stored in the 0-31 range of ASCII in the font
S
Sean Barrett 已提交
835
static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28, };
S
Sean Barrett 已提交
836

S
Sean Barrett 已提交
837 838 839 840 841 842 843
enum
{
   STBTE__propmode_default,
   STBTE__propmode_always,
   STBTE__propmode_never,
};

S
Sean Barrett 已提交
844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871
enum
{
   STBTE__paint,

   // from here down does hittesting
   STBTE__tick,
   STBTE__mousemove,
   STBTE__mousewheel,
   STBTE__leftdown,
   STBTE__leftup,
   STBTE__rightdown,
   STBTE__rightup,
};

typedef struct
{
   int expanded, mode;
   int delta_height;     // number of rows they've requested for this
   int side;
   int width,height;
   int x0,y0;
} stbte__panel;

typedef struct
{
   int x0,y0,x1,y1,color;
} stbte__colorrect;

S
Sean Barrett 已提交
872 873
#define STBTE__MAX_DELAYRECT 256

S
Sean Barrett 已提交
874 875 876 877 878
typedef struct
{
   int tool, active_event;
   int active_id, hot_id, next_hot_id;
   int event;
S
Sean Barrett 已提交
879
   int mx,my, dx,dy;
S
Sean Barrett 已提交
880 881 882 883
   int ms_time;
   int shift, scrollkey;
   int initted;
   int side_extended[2];
S
Sean Barrett 已提交
884
   stbte__colorrect delayrect[STBTE__MAX_DELAYRECT];
S
Sean Barrett 已提交
885
   int delaycount;
S
Sean Barrett 已提交
886
   int show_grid, show_links;
S
Sean Barrett 已提交
887 888 889 890
   int brush_state; // used to decide which kind of erasing
   int eyedrop_x, eyedrop_y, eyedrop_last_layer;
   int pasting, paste_x, paste_y;
   int scrolling, start_x, start_y;
S
Sean Barrett 已提交
891 892 893
   int last_mouse_x, last_mouse_y;
   int accum_x, accum_y;
   int linking;
S
Sean Barrett 已提交
894 895 896 897 898 899 900 901 902 903 904 905
   int dragging;
   int drag_x, drag_y, drag_w, drag_h;
   int drag_offx, drag_offy, drag_dest_x, drag_dest_y;
   int undoing;
   int has_selection, select_x0, select_y0, select_x1, select_y1;
   int sx,sy;
   int x0,y0,x1,y1, left_width, right_width; // configurable widths
   float alert_timer;
   const char *alert_msg;
   float dt;
   stbte__panel panel[STBTE__num_panel];
   short copybuffer[STBTE_MAX_COPY][STBTE_MAX_LAYERS];
S
Sean Barrett 已提交
906 907 908 909 910 911 912
   float copyprops[STBTE_MAX_COPY][STBTE_MAX_PROPERTIES];
#ifdef STBTE_ALLOW_LINK
   stbte__link copylinks[STBTE_MAX_COPY];
#endif
   int copy_src_x, copy_src_y;
   stbte_tilemap *copy_src;
   int copy_width,copy_height,has_copy,copy_has_props;
S
Sean Barrett 已提交
913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941
} stbte__ui_t;

// there's only one UI system at a time, so we can globalize this
static stbte__ui_t stbte__ui = { STBTE__tool_brush, 0 };

#define STBTE__INACTIVE()     (stbte__ui.active_id == 0)
#define STBTE__IS_ACTIVE(id)  (stbte__ui.active_id == (id))
#define STBTE__IS_HOT(id)     (stbte__ui.hot_id    == (id))

#define STBTE__BUTTON_HEIGHT            (STBTE__FONT_HEIGHT + 2 * STBTE__BUTTON_INTERNAL_SPACING)
#define STBTE__BUTTON_INTERNAL_SPACING  (2 + (STBTE__FONT_HEIGHT>>4))

typedef struct
{
   const char *name;
   int locked;
   int hidden;
} stbte__layer;

enum
{
   STBTE__unlocked,
   STBTE__protected,
   STBTE__locked,
};

struct stbte_tilemap
{
    stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS];
S
Sean Barrett 已提交
942
    float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROPERTIES];
S
Sean Barrett 已提交
943 944 945 946
    #ifdef STBTE_ALLOW_LINK
    stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
    int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
    #endif
S
Sean Barrett 已提交
947 948 949 950 951 952 953 954 955
    int max_x, max_y, num_layers;
    int spacing_x, spacing_y;
    int palette_spacing_x, palette_spacing_y;
    int scroll_x,scroll_y;
    int cur_category, cur_tile, cur_layer;
    char *categories[STBTE_MAX_CATEGORIES];
    int num_categories, category_scroll;
    stbte__tileinfo *tiles;
    int num_tiles, max_tiles, digits;
S
Sean Barrett 已提交
956 957 958 959
    unsigned char undo_available_valid;
    unsigned char undo_available;
    unsigned char redo_available;
    unsigned char padding;
S
Sean Barrett 已提交
960 961 962 963 964
    int cur_palette_count;
    int palette_scroll;
    int tileinfo_dirty;
    stbte__layer layerinfo[STBTE_MAX_LAYERS];
    int has_layer_names;
965
    int layername_width;
S
Sean Barrett 已提交
966
    int layer_scroll;
S
Sean Barrett 已提交
967
    int propmode;
S
Sean Barrett 已提交
968 969 970 971 972 973 974
    int solo_layer;
    int undo_pos, undo_len, redo_len;
    short background_tile;
    unsigned char id_in_use[32768>>3];
    short *undo_buffer;
};

S
Sean Barrett 已提交
975
static char *default_category = (char*) "[unassigned]";
S
Sean Barrett 已提交
976 977 978 979 980 981

static void stbte__init_gui(void)
{
   int i,n;
   stbte__ui.initted = 1;
   // init UI state
S
Sean Barrett 已提交
982
   stbte__ui.show_links = 1;
S
Sean Barrett 已提交
983 984 985 986 987
   for (i=0; i < STBTE__num_panel; ++i) {
      stbte__ui.panel[i].expanded     = 1; // visible if not autohidden
      stbte__ui.panel[i].delta_height = 0;
      stbte__ui.panel[i].side         = STBTE__side_left;
   }
S
Sean Barrett 已提交
988 989
   stbte__ui.panel[STBTE__panel_toolbar  ].side = STBTE__side_top;
   stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right;
S
Sean Barrett 已提交
990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013

   if (stbte__ui.left_width == 0)
      stbte__ui.left_width = 80;
   if (stbte__ui.right_width == 0)
      stbte__ui.right_width = 80;

   // init font
   n=95+16;
   for (i=0; i < 95+16; ++i) {
      stbte__font_offset[i] = n;
      n += stbte__fontdata[i];
   }
}

stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles)
{
   int i;
   stbte_tilemap *tm;
   STBTE_ASSERT(map_layers >= 0 && map_layers <= STBTE_MAX_LAYERS);
   STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
   STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
   if (map_x < 0 || map_y < 0 || map_layers < 0 ||
       map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y || map_layers > STBTE_MAX_LAYERS)
      return NULL;
S
Sean Barrett 已提交
1014

S
Sean Barrett 已提交
1015 1016 1017 1018 1019 1020 1021 1022
   if (!stbte__ui.initted)
      stbte__init_gui();

   tm = (stbte_tilemap *) malloc(sizeof(*tm) + sizeof(*tm->tiles) * max_tiles + STBTE_UNDO_BUFFER_BYTES);
   if (tm == NULL)
      return NULL;

   tm->tiles = (stbte__tileinfo *) (tm+1);
S
Sean Barrett 已提交
1023
   tm->undo_buffer = (short *) (tm->tiles + max_tiles);
S
Sean Barrett 已提交
1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
   tm->num_layers = map_layers;
   tm->max_x = map_x;
   tm->max_y = map_y;
   tm->spacing_x = spacing_x;
   tm->spacing_y = spacing_y;
   tm->scroll_x = 0;
   tm->scroll_y = 0;
   tm->palette_scroll = 0;
   tm->palette_spacing_x = spacing_x+1;
   tm->palette_spacing_y = spacing_y+1;
   tm->cur_category = -1;
   tm->cur_tile = 0;
   tm->solo_layer = -1;
   tm->undo_len = 0;
   tm->redo_len = 0;
   tm->undo_pos = 0;
   tm->category_scroll = 0;
   tm->layer_scroll = 0;
S
Sean Barrett 已提交
1042
   tm->propmode = 0;
S
Sean Barrett 已提交
1043
   tm->has_layer_names = 0;
1044
   tm->layername_width = 0;
S
Sean Barrett 已提交
1045
   tm->undo_available_valid = 0;
S
Sean Barrett 已提交
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066

   for (i=0; i < tm->num_layers; ++i) {
      tm->layerinfo[i].hidden = 0;
      tm->layerinfo[i].locked = STBTE__unlocked;
      tm->layerinfo[i].name   = 0;
   }

   tm->background_tile = STBTE__NO_TILE;
   stbte_clear_map(tm);

   tm->max_tiles = max_tiles;
   tm->num_tiles = 0;
   for (i=0; i < 32768/8; ++i)
      tm->id_in_use[i] = 0;
   tm->tileinfo_dirty = 1;
   return tm;
}

void stbte_set_background_tile(stbte_tilemap *tm, short id)
{
   int i;
S
Sean Barrett 已提交
1067 1068 1069
   STBTE_ASSERT(id >= -1);
   // STBTE_ASSERT(id < 32768);
   if (id < -1)
S
Sean Barrett 已提交
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
      return;
   for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i)
      if (tm->data[0][i][0] == -1)
         tm->data[0][i][0] = id;
   tm->background_tile = id;
}

void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y)
{
   tm->spacing_x = spacing_x;
   tm->spacing_y = spacing_y;
   tm->palette_spacing_x = palette_spacing_x;
   tm->palette_spacing_y = palette_spacing_y;
}

void stbte_set_sidewidths(int left, int right)
{
   stbte__ui.left_width  = left;
   stbte__ui.right_width = right;
}

void stbte_set_display(int x0, int y0, int x1, int y1)
{
   stbte__ui.x0 = x0;
   stbte__ui.y0 = y0;
   stbte__ui.x1 = x1;
   stbte__ui.y1 = y1;
}

void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category_c)
{
   char *category = (char *) category_c;
   STBTE_ASSERT(id < 32768);
   STBTE_ASSERT(tm->num_tiles < tm->max_tiles);
   STBTE_ASSERT((tm->id_in_use[id>>3]&(1<<(id&7))) == 0);
   if (id >= 32768 || tm->num_tiles >= tm->max_tiles || (tm->id_in_use[id>>3]&(1<<(id&7))))
      return;

   if (category == NULL)
      category = (char*) default_category;
   tm->id_in_use[id>>3] |= 1 << (id&7);
   tm->tiles[tm->num_tiles].category    = category;
   tm->tiles[tm->num_tiles].id        = id;
   tm->tiles[tm->num_tiles].layermask = layermask;
   ++tm->num_tiles;
   tm->tileinfo_dirty = 1;
}

1118 1119
static int stbte__text_width(const char *str);

S
Sean Barrett 已提交
1120 1121 1122 1123
void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername)
{
   STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
   if (layer >= 0 && layer < tm->num_layers) {
S
Sean Barrett 已提交
1124
      int width;
S
Sean Barrett 已提交
1125 1126
      tm->layerinfo[layer].name = layername;
      tm->has_layer_names = 1;
S
Sean Barrett 已提交
1127
      width = stbte__text_width(layername);
1128
      tm->layername_width = (width > tm->layername_width ? width : tm->layername_width);
S
Sean Barrett 已提交
1129 1130 1131 1132 1133 1134 1135 1136 1137
   }
}

void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y)
{
   *max_x = tm->max_x;
   *max_y = tm->max_y;
}

S
Sean Barrett 已提交
1138
short* stbte_get_tile(stbte_tilemap *tm, int x, int y)
S
Sean Barrett 已提交
1139 1140 1141 1142 1143 1144 1145
{
   STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
   if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
      return NULL;
   return tm->data[y][x];
}

S
Sean Barrett 已提交
1146 1147 1148 1149 1150 1151 1152 1153 1154 1155
float *stbte_get_properties(stbte_tilemap *tm, int x, int y)
{
   STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
   if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
      return NULL;
   return tm->props[y][x];
}

void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty)
{
S
Sean Barrett 已提交
1156
   int gx=-1,gy=-1;
S
Sean Barrett 已提交
1157 1158 1159
   STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
#ifdef STBTE_ALLOW_LINK
   if (x >= 0 && x < STBTE_MAX_TILEMAP_X && y >= 0 && y < STBTE_MAX_TILEMAP_Y) {
S
Sean Barrett 已提交
1160 1161 1162 1163 1164
      gx = tm->link[y][x].x;
      gy = tm->link[y][x].y;
      if (gx >= 0)
         if (!STBTE_ALLOW_LINK(tm->data[y][x], tm->props[y][x], tm->data[gy][gx], tm->props[gy][gx]))
            gx = gy = -1;
S
Sean Barrett 已提交
1165 1166
   }
#endif
S
Sean Barrett 已提交
1167 1168
   *destx = gx;
   *desty = gy;
S
Sean Barrett 已提交
1169 1170
}

S
Sean Barrett 已提交
1171 1172 1173 1174 1175
void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val)
{
   tm->props[y][x][n] = val;
}

S
Sean Barrett 已提交
1176
#ifdef STBTE_ALLOW_LINK
S
Sean Barrett 已提交
1177
static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode);
S
Sean Barrett 已提交
1178
#endif
S
Sean Barrett 已提交
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194

enum
{
   STBTE__undo_none,
   STBTE__undo_record,
   STBTE__undo_block,
};

void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty)
{
#ifdef STBTE_ALLOW_LINK
   stbte__set_link(tm, x, y, destx, desty, STBTE__undo_none);
#else
   STBTE_ASSERT(0);
#endif
}
S
Sean Barrett 已提交
1195 1196


S
Sean Barrett 已提交
1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214 1215 1216
// returns an array of map_layers shorts. each short is either
// one of the tile_id values from define_tile, or STBTE_EMPTY

void stbte_set_dimensions(stbte_tilemap *tm, int map_x, int map_y)
{
   STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
   STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
   if (map_x < 0 || map_y < 0 || map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y)
      return;
   tm->max_x = map_x;
   tm->max_y = map_y;
}

void stbte_clear_map(stbte_tilemap *tm)
{
   int i,j;
   for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) {
      tm->data[0][i][0] = tm->background_tile;
      for (j=1; j < tm->num_layers; ++j)
         tm->data[0][i][j] = STBTE__NO_TILE;
S
Sean Barrett 已提交
1217
      for (j=0; j < STBTE_MAX_PROPERTIES; ++j)
S
Sean Barrett 已提交
1218 1219 1220 1221 1222 1223
         tm->props[0][i][j] = 0;
      #ifdef STBTE_ALLOW_LINK
      tm->link[0][i].x = -1;
      tm->link[0][i].y = -1;
      tm->linkcount[0][i] = 0;
      #endif
S
Sean Barrett 已提交
1224 1225 1226 1227 1228 1229 1230
   }
}

void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile)
{
   STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
   STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
S
Sean Barrett 已提交
1231 1232
   STBTE_ASSERT(tile >= -1);
   //STBTE_ASSERT(tile < 32768);
S
Sean Barrett 已提交
1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259
   if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
      return;
   if (layer < 0 || layer >= tm->num_layers || tile < -1)
      return;
   tm->data[y][x][layer] = tile;
}

static void stbte__choose_category(stbte_tilemap *tm, int category)
{
   int i,n=0;
   tm->cur_category = category;
   for (i=0; i < tm->num_tiles; ++i)
      if (tm->tiles[i].category_id == category || category == -1)
         ++n;
   tm->cur_palette_count = n;
   tm->palette_scroll = 0;
}

static int stbte__strequal(char *p, char *q)
{
   while (*p)
      if (*p++ != *q++) return 0;
   return *q == 0;
}

static void stbte__compute_tileinfo(stbte_tilemap *tm)
{
S
Sean Barrett 已提交
1260
   int i,j;
S
Sean Barrett 已提交
1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297

   tm->num_categories=0;

   for (i=0; i < tm->num_tiles; ++i) {
      stbte__tileinfo *t = &tm->tiles[i];
      // find category
      for (j=0; j < tm->num_categories; ++j)
         if (stbte__strequal(t->category, tm->categories[j]))
            goto found;
      tm->categories[j] = t->category;
      ++tm->num_categories;
     found:
      t->category_id = (unsigned short) j;
   }

   // currently number of categories can never decrease because you
   // can't remove tile definitions, but let's get it right anyway
   if (tm->cur_category > tm->num_categories) {
      tm->cur_category = -1;
   }

   stbte__choose_category(tm, tm->cur_category);

   tm->tileinfo_dirty = 0;
}

static void stbte__prepare_tileinfo(stbte_tilemap *tm)
{
   if (tm->tileinfo_dirty)
      stbte__compute_tileinfo(tm);
}


/////////////////////// undo system ////////////////////////

// the undo system works by storing "commands" into a buffer, and
// then playing back those commands. undo and redo have to store
S
Sean Barrett 已提交
1298
// the commands in different order.
S
Sean Barrett 已提交
1299 1300 1301 1302 1303 1304 1305 1306 1307
//
// the commands are:
//
// 1)  end_of_undo_record
//       -1:short
//
// 2)  end_of_redo_record
//       -2:short
//
S
Sean Barrett 已提交
1308
// 3)  tile update
S
Sean Barrett 已提交
1309 1310
//       tile_id:short (-1..32767)
//       x_coord:short
S
Sean Barrett 已提交
1311
//       y_coord:short
S
Sean Barrett 已提交
1312 1313
//       layer:short (0..31)
//
S
Sean Barrett 已提交
1314 1315 1316 1317 1318 1319 1320
// 4)  property update (also used for links)
//       value_hi:short
//       value_lo:short
//       y_coord:short
//       x_coord:short
//       property:short (256+prop#)
//
S
Sean Barrett 已提交
1321 1322 1323 1324 1325 1326
// Since we use a circular buffer, we might overwrite the undo storage.
// To detect this, before playing back commands we scan back and see
// if we see an end_of_undo_record before hitting the relevant boundary,
// it's wholly contained.
//
// When we read back through, we see them in reverse order, so
S
Sean Barrett 已提交
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337
// we'll see the layer number or property number first
//
// To be clearer about the circular buffer, there are two cases:
//     1. a single record is larger than the whole buffer.
//        this is caught because the end_of_undo_record will
//        get overwritten.
//     2. multiple records written are larger than the whole
//        buffer, so some of them have been overwritten by
//        the later ones. this is handled by explicitly tracking
//        the undo length; we never try to parse the data that
//        got overwritten
S
Sean Barrett 已提交
1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352

// given two points, compute the length between them
#define stbte__wrap(pos)            ((pos) & (STBTE__UNDO_BUFFER_COUNT-1))

#define STBTE__undo_record  -2
#define STBTE__redo_record  -3
#define STBTE__undo_junk    -4  // this is written underneath the undo pointer, never used

static void stbte__write_undo(stbte_tilemap *tm, short value)
{
   int pos = tm->undo_pos;
   tm->undo_buffer[pos] = value;
   tm->undo_pos = stbte__wrap(pos+1);
   tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2);
   tm->redo_len -= (tm->redo_len > 0);
S
Sean Barrett 已提交
1353
   tm->undo_available_valid = 0;
S
Sean Barrett 已提交
1354 1355 1356 1357 1358 1359 1360 1361 1362
}

static void stbte__write_redo(stbte_tilemap *tm, short value)
{
   int pos = tm->undo_pos;
   tm->undo_buffer[pos] = value;
   tm->undo_pos = stbte__wrap(pos-1);
   tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2);
   tm->undo_len -= (tm->undo_len > 0);
S
Sean Barrett 已提交
1363
   tm->undo_available_valid = 0;
S
Sean Barrett 已提交
1364 1365 1366 1367 1368 1369 1370 1371 1372 1373 1374 1375 1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402 1403 1404 1405 1406 1407 1408 1409 1410
}

static void stbte__begin_undo(stbte_tilemap *tm)
{
   tm->redo_len = 0;
   stbte__write_undo(tm, STBTE__undo_record);
   stbte__ui.undoing = 1;
   stbte__ui.alert_msg = 0; // clear alert if they start doing something
}

static void stbte__end_undo(stbte_tilemap *tm)
{
   if (stbte__ui.undoing) {
      // check if anything got written
      int pos = stbte__wrap(tm->undo_pos-1);
      if (tm->undo_buffer[pos] == STBTE__undo_record) {
         // empty undo record, move back
         tm->undo_pos = pos;
         STBTE_ASSERT(tm->undo_len > 0);
         tm->undo_len -= 1;
      }
      tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
      // otherwise do nothing

      stbte__ui.undoing = 0;
   }
}

static void stbte__undo_record(stbte_tilemap *tm, int x, int y, int i, int v)
{
   STBTE_ASSERT(stbte__ui.undoing);
   if (stbte__ui.undoing) {
      stbte__write_undo(tm, v);
      stbte__write_undo(tm, x);
      stbte__write_undo(tm, y);
      stbte__write_undo(tm, i);
   }
}

static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v)
{
   stbte__write_redo(tm, v);
   stbte__write_redo(tm, x);
   stbte__write_redo(tm, y);
   stbte__write_redo(tm, i);
}

S
Sean Barrett 已提交
1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425 1426 1427 1428 1429 1430 1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453
static float stbte__extract_float(short s0, short s1)
{
   union { float f; short s[2]; } converter;
   converter.s[0] = s0;
   converter.s[1] = s1;
   return converter.f;
}

static short stbte__extract_short(float f, int slot)
{
   union { float f; short s[2]; } converter;
   converter.f = f;
   return converter.s[slot];
}

static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
{
   STBTE_ASSERT(stbte__ui.undoing);
   if (stbte__ui.undoing) {
      stbte__write_undo(tm, s1);
      stbte__write_undo(tm, s0);
      stbte__write_undo(tm, x);
      stbte__write_undo(tm, y);
      stbte__write_undo(tm, 256+i);
   }
}

static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f)
{
   stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1));
}

static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
{
   stbte__write_redo(tm, s1);
   stbte__write_redo(tm, s0);
   stbte__write_redo(tm, x);
   stbte__write_redo(tm, y);
   stbte__write_redo(tm, 256+i);
}


static int stbte__undo_find_end(stbte_tilemap *tm)
S
Sean Barrett 已提交
1454 1455
{
   // first scan through for the end record
S
Sean Barrett 已提交
1456 1457
   int i, pos = stbte__wrap(tm->undo_pos-1);
   for (i=0; i < tm->undo_len;) {
S
Sean Barrett 已提交
1458 1459 1460
      STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
      if (tm->undo_buffer[pos] == STBTE__undo_record)
         break;
S
Sean Barrett 已提交
1461 1462 1463 1464
      if (tm->undo_buffer[pos] >= 255)
         pos = stbte__wrap(pos-5), i += 5;
      else
         pos = stbte__wrap(pos-4), i += 4;
S
Sean Barrett 已提交
1465 1466
   }
   if (i >= tm->undo_len)
S
Sean Barrett 已提交
1467 1468 1469 1470 1471 1472 1473 1474 1475
      return -1;
   return pos;
}

static void stbte__undo(stbte_tilemap *tm)
{
   int i, pos, endpos;
   endpos = stbte__undo_find_end(tm);
   if (endpos < 0)
S
Sean Barrett 已提交
1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492
      return;

   // we found a complete undo record
   pos = stbte__wrap(tm->undo_pos-1);

   // start a redo record
   stbte__write_redo(tm, STBTE__redo_record);

   // so now go back through undo and apply in reverse
   // order, and copy it to redo
   for (i=0; endpos != pos; i += 4) {
      int x,y,n,v;
      // get the undo entry
      n = tm->undo_buffer[pos];
      y = tm->undo_buffer[stbte__wrap(pos-1)];
      x = tm->undo_buffer[stbte__wrap(pos-2)];
      v = tm->undo_buffer[stbte__wrap(pos-3)];
S
Sean Barrett 已提交
1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505
      if (n >= 255) {
         short s0=0,s1=0;
         int v2 = tm->undo_buffer[stbte__wrap(pos-4)];
         pos = stbte__wrap(pos-5);
         if (n > 255) {
            float vf = stbte__extract_float(v, v2);
            s0 = stbte__extract_short(tm->props[y][x][n-256], 0);
            s1 = stbte__extract_short(tm->props[y][x][n-256], 1);
            tm->props[y][x][n-256] = vf;
         } else {
#ifdef STBTE_ALLOW_LINK
            s0 = tm->link[y][x].x;
            s1 = tm->link[y][x].y;
S
Sean Barrett 已提交
1506
            stbte__set_link(tm, x,y, v, v2, STBTE__undo_none);
S
Sean Barrett 已提交
1507 1508 1509 1510 1511 1512 1513 1514 1515 1516 1517 1518
#endif
         }
         // write the redo entry
         stbte__redo_record_prop(tm, x, y, n-256, s0,s1);
         // apply the undo entry
      } else {
         pos = stbte__wrap(pos-4);
         // write the redo entry
         stbte__redo_record(tm, x, y, n, tm->data[y][x][n]);
         // apply the undo entry
         tm->data[y][x][n] = (short) v;
      }
S
Sean Barrett 已提交
1519 1520 1521 1522 1523
   }
   // overwrite undo record with junk
   tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
}

S
Sean Barrett 已提交
1524
static int stbte__redo_find_end(stbte_tilemap *tm)
S
Sean Barrett 已提交
1525 1526
{
   // first scan through for the end record
S
Sean Barrett 已提交
1527 1528
   int i, pos = stbte__wrap(tm->undo_pos+1);
   for (i=0; i < tm->redo_len;) {
S
Sean Barrett 已提交
1529 1530 1531
      STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
      if (tm->undo_buffer[pos] == STBTE__redo_record)
         break;
S
Sean Barrett 已提交
1532 1533 1534 1535
      if (tm->undo_buffer[pos] >= 255)
         pos = stbte__wrap(pos+5), i += 5;
      else
         pos = stbte__wrap(pos+4), i += 4;
S
Sean Barrett 已提交
1536 1537
   }
   if (i >= tm->redo_len)
S
Sean Barrett 已提交
1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548
      return -1; // this should only ever happen if redo buffer is empty
   return pos;
}

static void stbte__redo(stbte_tilemap *tm)
{
   // first scan through for the end record
   int i, pos, endpos;
   endpos = stbte__redo_find_end(tm);
   if (endpos < 0)
      return;
S
Sean Barrett 已提交
1549 1550 1551

   // we found a complete redo record
   pos = stbte__wrap(tm->undo_pos+1);
S
Sean Barrett 已提交
1552

S
Sean Barrett 已提交
1553 1554 1555 1556 1557 1558 1559 1560 1561
   // start an undo record
   stbte__write_undo(tm, STBTE__undo_record);

   for (i=0; pos != endpos; i += 4) {
      int x,y,n,v;
      n = tm->undo_buffer[pos];
      y = tm->undo_buffer[stbte__wrap(pos+1)];
      x = tm->undo_buffer[stbte__wrap(pos+2)];
      v = tm->undo_buffer[stbte__wrap(pos+3)];
S
Sean Barrett 已提交
1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574
      if (n >= 255) {
         int v2 = tm->undo_buffer[stbte__wrap(pos+4)];
         short s0=0,s1=0;
         pos = stbte__wrap(pos+5);
         if (n > 255) {
            float vf = stbte__extract_float(v, v2);
            s0 = stbte__extract_short(tm->props[y][x][n-256],0);
            s1 = stbte__extract_short(tm->props[y][x][n-256],1);
            tm->props[y][x][n-256] = vf;
         }