Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4172 lines
143 KiB

  1. // stb_tilemap_editor.h - v0.38 - Sean Barrett - http://nothings.org/stb
  2. // placed in the public domain - not copyrighted - first released 2014-09
  3. //
  4. // Embeddable tilemap editor for C/C++
  5. //
  6. //
  7. // TABLE OF CONTENTS
  8. // FAQ
  9. // How to compile/use the library
  10. // Additional configuration macros
  11. // API documentation
  12. // Info on editing multiple levels
  13. // Revision history
  14. // Todo
  15. // Credits
  16. // License
  17. //
  18. //
  19. // FAQ
  20. //
  21. // Q: What counts as a tilemap for this library?
  22. //
  23. // A: An array of rectangles, where each rectangle contains a small
  24. // stack of images.
  25. //
  26. // Q: What are the limitations?
  27. //
  28. // A: Maps are limited to 4096x4096 in dimension.
  29. // Each map square can only contain a stack of at most 32 images.
  30. // A map can only use up to 32768 distinct image tiles.
  31. //
  32. // Q: How do I compile this?
  33. //
  34. // A: You need to #define several symbols before #including it, but only
  35. // in one file. This will cause all the function definitions to be
  36. // generated in that file. See the "HOW TO COMPILE" section.
  37. //
  38. // Q: What advantages does this have over a standalone editor?
  39. //
  40. // A: For one, you can integrate the editor into your game so you can
  41. // flip between editing and testing without even switching windows.
  42. // For another, you don't need an XML parser to get at the map data.
  43. //
  44. // Q: Can I live-edit my game maps?
  45. //
  46. // A: Not really, the editor keeps its own map representation.
  47. //
  48. // Q: How do I save and load maps?
  49. //
  50. // A: You have to do this yourself. The editor provides serialization
  51. // functions (get & set) for reading and writing the map it holds.
  52. // You can choose whatever format you want to store the map to on
  53. // disk; you just need to provide functions to convert. (For example,
  54. // I actually store the editor's map representation to disk basically
  55. // as-is; then I have a single function that converts from the editor
  56. // map representation to the game representation, which is used both
  57. // to go from editor-to-game and from loaded-map-to-game.)
  58. //
  59. // Q: I want to have tiles change appearance based on what's
  60. // adjacent, or other tile-display/substitution trickiness.
  61. //
  62. // A: You can do this when you convert from the editor's map
  63. // representation to the game representation, but there's
  64. // no way to show this live in the editor.
  65. //
  66. // Q: The editor appears to be put map location (0,0) at the top left?
  67. // I want to use a different coordinate system in my game (e.g. y
  68. // increasing upwards, or origin at the center).
  69. //
  70. // A: You can do this when you convert from the editor's map
  71. // representation to the game representation. (Don't forget to
  72. // translate link coordinates as well!)
  73. //
  74. // Q: The editor appears to put pixel (0,0) at the top left? I want
  75. // to use a different coordinate system in my game.
  76. //
  77. // A: The editor defines an "editor pixel coordinate system" with
  78. // (0,0) at the top left and requires you to display things in
  79. // that coordinate system. You can freely remap those coordinates
  80. // to anything you want on screen.
  81. //
  82. // Q: How do I scale the user interface?
  83. //
  84. // A: Since you do all the rendering, you can scale up all the rendering
  85. // calls that the library makes to you. If you do, (a) you need
  86. // to also scale up the mouse coordinates, and (b) you may want
  87. // to scale the map display back down so that you're only scaling
  88. // the UI and not everything. See the next question.
  89. //
  90. // Q: How do I scale the map display?
  91. //
  92. // A: Use stbte_set_spacing() to change the size that the map is displayed
  93. // at. Note that the "callbacks" to draw tiles are used for both drawing
  94. // the map and drawing the tile palette, so that callback may need to
  95. // draw at two different scales. You should choose the scales to match
  96. // You can tell them apart because the
  97. // tile palette gets NULL for the property pointer.
  98. //
  99. // Q: How does object editing work?
  100. //
  101. // A: One way to think of this is that in the editor, you're placing
  102. // spawners, not objects. Each spawner must be tile-aligned, because
  103. // it's only a tile editor. Each tile (stack of layers) gets
  104. // an associated set of properties, and it's up to you to
  105. // determine what properties should appear for a given tile,
  106. // based on e.g. the spawners that are in it.
  107. //
  108. // Q: How are properties themselves handled?
  109. //
  110. // A: All properties, regardless of UI behavior, are internally floats.
  111. // Each tile has an array of floats associated with it, which is
  112. // passed back to you when drawing the tiles so you can draw
  113. // objects appropriately modified by the properties.
  114. //
  115. // Q: What if I want to have two different objects/spawners in
  116. // one tile, both of which have their own properties?
  117. //
  118. // A: Make sure STBTE_MAX_PROPERTIES is large enough for the sum of
  119. // properties in both objects, and then you have to explicitly
  120. // map the property slot #s to the appropriate objects. They'll
  121. // still all appear in a single property panel; there's no way
  122. // to get multiple panels.
  123. //
  124. // Q: Can I do one-to-many linking?
  125. //
  126. // A: The library only supports one link per tile. However, you
  127. // can have multiple tiles all link to a single tile. So, you
  128. // can fake one-to-many linking by linking in the reverse
  129. // direction.
  130. //
  131. // Q: What if I have two objects in the same tile, and they each
  132. // need an independent link? Or I have two kinds of link associated
  133. // with a single object?
  134. //
  135. // A: There is no way to do this. (Unless you can reverse one link.)
  136. //
  137. // Q: How does cut & paste interact with object properties & links?
  138. //
  139. // A: Currently the library has no idea which properties or links
  140. // are associated with which layers of a tile. So currently, the
  141. // library will only copy properties & links if the layer panel
  142. // is set to allow all layers to be copied, OR if you set the
  143. // "props" in the layer panel to "always". Similarly, you can
  144. // set "props" to "none" so it will never copy.
  145. //
  146. // Q: What happens if the library gets a memory allocation failure
  147. // while I'm editing? Will I lose my work?
  148. //
  149. // A: The library allocates all editor memory when you create
  150. // the tilemap. It allocates a maximally-sized map and a
  151. // fixed-size undo buffer (and the fixed-size copy buffer
  152. // is static), and never allocates memory while it's running.
  153. // So it can't fail due to running out of memory.
  154. //
  155. // Q: What happens if the library crashes while I'm editing? Will
  156. // I lose my work?
  157. //
  158. // A: Yes. Save often.
  159. //
  160. //
  161. // HOW TO COMPILE
  162. //
  163. // This header file contains both the header file and the
  164. // implementation file in one. To create the implementation,
  165. // in one source file define a few symbols first and then
  166. // include this header:
  167. //
  168. // #define STB_TILEMAP_EDITOR_IMPLEMENTATION
  169. // // this triggers the implementation
  170. //
  171. // void STBTE_DRAW_RECT(int x0, int y0, int x1, int y1, uint color);
  172. // // this must draw a filled rectangle (exclusive on right/bottom)
  173. // // color = (r<<16)|(g<<8)|(b)
  174. //
  175. // void STBTE_DRAW_TILE(int x0, int y0,
  176. // unsigned short id, int highlight, float *data);
  177. // // this draws the tile image identified by 'id' in one of several
  178. // // highlight modes (see STBTE_drawmode_* in the header section);
  179. // // if 'data' is NULL, it's drawing the tile in the palette; if 'data'
  180. // // is not NULL, it's drawing a tile on the map, and that is the data
  181. // // associated with that map tile
  182. //
  183. // #include "stb_tilemap_editor.h"
  184. //
  185. // Optionally you can define the following functions before the include;
  186. // note these must be macros (but they can just call a function) so
  187. // this library can #ifdef to detect if you've defined them:
  188. //
  189. // #define STBTE_PROP_TYPE(int n, short *tiledata, float *params) ...
  190. // // Returns the type of the n'th property of a given tile, which
  191. // // controls how it is edited. Legal types are:
  192. // // 0 /* no editable property in this slot */
  193. // // STBTE_PROP_int /* uses a slider to adjust value */
  194. // // STBTE_PROP_float /* uses a weird multi-axis control */
  195. // // STBTE_PROP_bool /* uses a checkbox to change value */
  196. // // And you can bitwise-OR in the following flags:
  197. // // STBTE_PROP_disabled
  198. // // Note that all of these are stored as floats in the param array.
  199. // // The integer slider is limited in precision based on the space
  200. // // available on screen, so for wide-ranged integers you may want
  201. // // to use floats instead.
  202. // //
  203. // // Since the tiledata is passed to you, you can choose which property
  204. // // is bound to that slot based on that data.
  205. // //
  206. // // Changing the type of a parameter does not cause the underlying
  207. // // value to be clamped to the type min/max except when the tile is
  208. // // explicitly selected.
  209. //
  210. // #define STBTE_PROP_NAME(int n, short *tiledata, float *params) ...
  211. // // these return a string with the name for slot #n in the float
  212. // // property list for the tile.
  213. //
  214. // #define STBTE_PROP_MIN(int n, short *tiledata) ...your code here...
  215. // #define STBTE_PROP_MAX(int n, short *tiledata) ...your code here...
  216. // // These return the allowable range for the property values for
  217. // // the specified slot. It is never called for boolean types.
  218. //
  219. // #define STBTE_PROP_FLOAT_SCALE(int n, short *tiledata, float *params)
  220. // // This rescales the float control for a given property; by default
  221. // // left mouse drags add integers, right mouse drags adds fractions,
  222. // // but you can rescale this per-property.
  223. //
  224. // #define STBTE_FLOAT_CONTROL_GRANULARITY ... value ...
  225. // // This returns the number of pixels of mouse motion necessary
  226. // // to advance the object float control. Default is 4
  227. //
  228. // #define STBTE_ALLOW_LINK(short *src, float *src_data, \
  229. // short *dest, float *dest_data) ...your code...
  230. // // this returns true or false depending on whether you allow a link
  231. // // to be drawn from a tile 'src' to a tile 'dest'. if you don't
  232. // // define this, linking will not be supported
  233. //
  234. // #define STBTE_LINK_COLOR(short *src, float *src_data, \
  235. // short *dest, float *dest_data) ...your code...
  236. // // return a color encoded as a 24-bit unsigned integer in the
  237. // // form 0xRRGGBB. If you don't define this, default colors will
  238. // // be used.
  239. //
  240. //
  241. // [[ support for those below is not implemented yet ]]
  242. //
  243. // #define STBTE_HITTEST_TILE(x0,y0,id,mx,my) ...your code here...
  244. // // this returns true or false depending on whether the mouse
  245. // // pointer at mx,my is over (touching) a tile of type 'id'
  246. // // displayed at x0,y0. Normally stb_tilemap_editor just does
  247. // // this hittest based on the tile geometry, but if you have
  248. // // tiles whose images extend out of the tile, you'll need this.
  249. //
  250. // ADDITIONAL CONFIGURATION
  251. //
  252. // The following symbols set static limits which determine how much
  253. // memory will be allocated for the editor. You can override them
  254. // by making similiar definitions, but memory usage will increase.
  255. //
  256. // #define STBTE_MAX_TILEMAP_X 200 // max 4096
  257. // #define STBTE_MAX_TILEMAP_Y 200 // max 4096
  258. // #define STBTE_MAX_LAYERS 8 // max 32
  259. // #define STBTE_MAX_CATEGORIES 100
  260. // #define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB
  261. // #define STBTE_MAX_COPY 90000 // e.g. 300x300
  262. // #define STBTE_MAX_PROPERTIES 10 // max properties per tile
  263. //
  264. // API
  265. //
  266. // Further documentation appears in the header-file section below.
  267. //
  268. // EDITING MULTIPLE LEVELS
  269. //
  270. // You can only have one active editor instance. To switch between multiple
  271. // levels, you can either store the levels in your own format and copy them
  272. // in and out of the editor format, or you can create multiple stbte_tilemap
  273. // objects and switch between them. The latter has the advantage that each
  274. // stbte_tilemap keeps its own undo state. (The clipboard is global, so
  275. // either approach allows cut&pasting between levels.)
  276. //
  277. // REVISION HISTORY
  278. // 0.38 fix warning
  279. // 0.37 fix warning
  280. // 0.36 minor compiler support
  281. // 0.35 layername button changes
  282. // - layername buttons grow with the layer panel
  283. // - fix stbte_create_map being declared as stbte_create
  284. // - fix declaration of stbte_create_map
  285. // 0.30 properties release
  286. // - properties panel for editing user-defined "object" properties
  287. // - can link each tile to one other tile
  288. // - keyboard interface
  289. // - fix eraser tool bug (worked in complex cases, failed in simple)
  290. // - undo/redo tools have visible disabled state
  291. // - tiles on higher layers draw on top of adjacent lower-layer tiles
  292. // 0.20 erasable release
  293. // - eraser tool
  294. // - fix bug when pasting into protected layer
  295. // - better color scheme
  296. // - internal-use color picker
  297. // 0.10 initial release
  298. //
  299. // TODO
  300. //
  301. // Separate scroll state for each category
  302. // Implement paint bucket
  303. // Support STBTE_HITTEST_TILE above
  304. // ?Cancel drags by clicking other button? - may be fixed
  305. // Finish support for toolbar at side
  306. //
  307. // CREDITS
  308. //
  309. //
  310. // Main editor & features
  311. // Sean Barrett
  312. // Additional features:
  313. // Josh Huelsman
  314. // Bugfixes:
  315. // Ryan Whitworth
  316. // Eugene Opalev
  317. //
  318. // LICENSE
  319. //
  320. // See end of file for license information.
  321. ///////////////////////////////////////////////////////////////////////
  322. //
  323. // HEADER SECTION
  324. #ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
  325. #define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
  326. #ifdef _WIN32
  327. #ifndef _CRT_SECURE_NO_WARNINGS
  328. #define _CRT_SECURE_NO_WARNINGS
  329. #endif
  330. #include <stdlib.h>
  331. #include <stdio.h>
  332. #endif
  333. typedef struct stbte_tilemap stbte_tilemap;
  334. // these are the drawmodes used in STBTE_DRAW_TILE
  335. enum
  336. {
  337. STBTE_drawmode_deemphasize = -1,
  338. STBTE_drawmode_normal = 0,
  339. STBTE_drawmode_emphasize = 1,
  340. };
  341. // these are the property types
  342. #define STBTE_PROP_none 0
  343. #define STBTE_PROP_int 1
  344. #define STBTE_PROP_float 2
  345. #define STBTE_PROP_bool 3
  346. #define STBTE_PROP_disabled 4
  347. ////////
  348. //
  349. // creation
  350. //
  351. extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles);
  352. // create an editable tilemap
  353. // map_x : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X
  354. // map_y : dimensions of map vertically (user can change this in editor) <= STBTE_MAX_TILEMAP_Y
  355. // map_layers : number of layers to use (fixed), <= STBTE_MAX_LAYERS
  356. // spacing_x : initial horizontal distance between left edges of map tiles in stb_tilemap_editor pixels
  357. // spacing_y : initial vertical distance between top edges of map tiles in stb_tilemap_editor pixels
  358. // max_tiles : maximum number of tiles that can defined
  359. //
  360. // If insufficient memory, returns NULL
  361. extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category);
  362. // call this repeatedly for each tile to install the tile definitions into the editable tilemap
  363. // tm : tilemap created by stbte_create_map
  364. // id : unique identifier for each tile, 0 <= id < 32768
  365. // layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7
  366. // (note that onscreen, the editor numbers the layers from 1 not 0)
  367. // layer 0 is the furthest back, layer 1 is just in front of layer 0, etc
  368. // category : which category this tile is grouped in
  369. extern void stbte_set_display(int x0, int y0, int x1, int y1);
  370. // call this once to set the size; if you resize, call it again
  371. /////////
  372. //
  373. // every frame
  374. //
  375. extern void stbte_draw(stbte_tilemap *tm);
  376. extern void stbte_tick(stbte_tilemap *tm, float time_in_seconds_since_last_frame);
  377. ////////////
  378. //
  379. // user input
  380. //
  381. // if you're using SDL, call the next function for SDL_MOUSEMOVE, SDL_MOUSEBUTTON, SDL_MOUSEWHEEL;
  382. // the transformation lets you scale from SDL mouse coords to stb_tilemap_editor coords
  383. extern void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xscale, float yscale, int xoffset, int yoffset);
  384. // otherwise, hook these up explicitly:
  385. extern void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey);
  386. extern void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey);
  387. extern void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll);
  388. // for keyboard, define your own mapping from keys to the following actions.
  389. // this is totally optional, as all features are accessible with the mouse
  390. enum stbte_action
  391. {
  392. STBTE_tool_select,
  393. STBTE_tool_brush,
  394. STBTE_tool_erase,
  395. STBTE_tool_rectangle,
  396. STBTE_tool_eyedropper,
  397. STBTE_tool_link,
  398. STBTE_act_toggle_grid,
  399. STBTE_act_toggle_links,
  400. STBTE_act_undo,
  401. STBTE_act_redo,
  402. STBTE_act_cut,
  403. STBTE_act_copy,
  404. STBTE_act_paste,
  405. STBTE_scroll_left,
  406. STBTE_scroll_right,
  407. STBTE_scroll_up,
  408. STBTE_scroll_down,
  409. };
  410. extern void stbte_action(stbte_tilemap *tm, enum stbte_action act);
  411. ////////////////
  412. //
  413. // save/load
  414. //
  415. // There is no editor file format. You have to save and load the data yourself
  416. // through the following functions. You can also use these functions to get the
  417. // data to generate game-formatted levels directly. (But make sure you save
  418. // first! You may also want to autosave to a temp file periodically, etc etc.)
  419. #define STBTE_EMPTY -1
  420. extern void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y);
  421. // get the dimensions of the level, since the user can change them
  422. extern short* stbte_get_tile(stbte_tilemap *tm, int x, int y);
  423. // returns an array of shorts that is 'map_layers' in length. each short is
  424. // either one of the tile_id values from define_tile, or STBTE_EMPTY.
  425. extern float *stbte_get_properties(stbte_tilemap *tm, int x, int y);
  426. // get the property array associated with the tile at x,y. this is an
  427. // array of floats that is STBTE_MAX_PROPERTIES in length; you have to
  428. // interpret the slots according to the semantics you've chosen
  429. extern void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty);
  430. // gets the link associated with the tile at x,y.
  431. extern void stbte_set_dimensions(stbte_tilemap *tm, int max_x, int max_y);
  432. // set the dimensions of the level, overrides previous stbte_create_map()
  433. // values or anything the user has changed
  434. extern void stbte_clear_map(stbte_tilemap *tm);
  435. // clears the map, including the region outside the defined region, so if the
  436. // user expands the map, they won't see garbage there
  437. extern void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile);
  438. // tile is your tile_id from define_tile, or STBTE_EMPTY
  439. extern void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val);
  440. // set the value of the n'th slot of the tile at x,y
  441. extern void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty);
  442. // set a link going from x,y to destx,desty. to force no link,
  443. // use destx=desty=-1
  444. ////////
  445. //
  446. // optional
  447. //
  448. extern void stbte_set_background_tile(stbte_tilemap *tm, short id);
  449. // selects the tile to fill the bottom layer with and used to clear bottom tiles to;
  450. // should be same ID as
  451. extern void stbte_set_sidewidths(int left, int right);
  452. // call this once to set the left & right side widths. don't call
  453. // it again since the user can change it
  454. extern void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y);
  455. // call this to set the spacing of map tiles and the spacing of palette tiles.
  456. // if you rescale your display, call it again (e.g. you can implement map zooming yourself)
  457. extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername);
  458. // sets a string name for your layer that shows in the layer selector. note that this
  459. // makes the layer selector wider. 'layer' is from 0..(map_layers-1)
  460. #endif
  461. #ifdef STB_TILEMAP_EDITOR_IMPLEMENTATION
  462. #ifndef STBTE_ASSERT
  463. #define STBTE_ASSERT assert
  464. #include <assert.h>
  465. #endif
  466. #ifdef _MSC_VER
  467. #define STBTE__NOTUSED(v) (void)(v)
  468. #else
  469. #define STBTE__NOTUSED(v) (void)sizeof(v)
  470. #endif
  471. #ifndef STBTE_MAX_TILEMAP_X
  472. #define STBTE_MAX_TILEMAP_X 200
  473. #endif
  474. #ifndef STBTE_MAX_TILEMAP_Y
  475. #define STBTE_MAX_TILEMAP_Y 200
  476. #endif
  477. #ifndef STBTE_MAX_LAYERS
  478. #define STBTE_MAX_LAYERS 8
  479. #endif
  480. #ifndef STBTE_MAX_CATEGORIES
  481. #define STBTE_MAX_CATEGORIES 100
  482. #endif
  483. #ifndef STBTE_MAX_COPY
  484. #define STBTE_MAX_COPY 65536
  485. #endif
  486. #ifndef STBTE_UNDO_BUFFER_BYTES
  487. #define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB
  488. #endif
  489. #ifndef STBTE_PROP_TYPE
  490. #define STBTE__NO_PROPS
  491. #define STBTE_PROP_TYPE(n,td,tp) 0
  492. #endif
  493. #ifndef STBTE_PROP_NAME
  494. #define STBTE_PROP_NAME(n,td,tp) ""
  495. #endif
  496. #ifndef STBTE_MAX_PROPERTIES
  497. #define STBTE_MAX_PROPERTIES 10
  498. #endif
  499. #ifndef STBTE_PROP_MIN
  500. #define STBTE_PROP_MIN(n,td,tp) 0
  501. #endif
  502. #ifndef STBTE_PROP_MAX
  503. #define STBTE_PROP_MAX(n,td,tp) 100.0
  504. #endif
  505. #ifndef STBTE_PROP_FLOAT_SCALE
  506. #define STBTE_PROP_FLOAT_SCALE(n,td,tp) 1 // default scale size
  507. #endif
  508. #ifndef STBTE_FLOAT_CONTROL_GRANULARITY
  509. #define STBTE_FLOAT_CONTROL_GRANULARITY 4
  510. #endif
  511. #define STBTE__UNDO_BUFFER_COUNT (STBTE_UNDO_BUFFER_BYTES>>1)
  512. #if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096
  513. #error "Maximum editable map size is 4096 x 4096"
  514. #endif
  515. #if STBTE_MAX_LAYERS > 32
  516. #error "Maximum layers allowed is 32"
  517. #endif
  518. #if STBTE_UNDO_BUFFER_COUNT & (STBTE_UNDO_BUFFER_COUNT-1)
  519. #error "Undo buffer size must be a power of 2"
  520. #endif
  521. #if STBTE_MAX_PROPERTIES == 0
  522. #define STBTE__NO_PROPS
  523. #endif
  524. #ifdef STBTE__NO_PROPS
  525. #undef STBTE_MAX_PROPERTIES
  526. #define STBTE_MAX_PROPERTIES 1 // so we can declare arrays
  527. #endif
  528. typedef struct
  529. {
  530. short x,y;
  531. } stbte__link;
  532. enum
  533. {
  534. STBTE__base,
  535. STBTE__outline,
  536. STBTE__text,
  537. STBTE__num_color_aspects,
  538. };
  539. enum
  540. {
  541. STBTE__idle,
  542. STBTE__over,
  543. STBTE__down,
  544. STBTE__over_down,
  545. STBTE__selected,
  546. STBTE__selected_over,
  547. STBTE__disabled,
  548. STBTE__num_color_states,
  549. };
  550. enum
  551. {
  552. STBTE__cexpander,
  553. STBTE__ctoolbar,
  554. STBTE__ctoolbar_button,
  555. STBTE__cpanel,
  556. STBTE__cpanel_sider,
  557. STBTE__cpanel_sizer,
  558. STBTE__cscrollbar,
  559. STBTE__cmapsize,
  560. STBTE__clayer_button,
  561. STBTE__clayer_hide,
  562. STBTE__clayer_lock,
  563. STBTE__clayer_solo,
  564. STBTE__ccategory_button,
  565. STBTE__num_color_modes,
  566. };
  567. #ifdef STBTE__COLORPICKER
  568. static char *stbte__color_names[] =
  569. {
  570. "expander", "toolbar", "tool button", "panel",
  571. "panel c1", "panel c2", "scollbar", "map button",
  572. "layer", "hide", "lock", "solo",
  573. "category",
  574. };
  575. #endif // STBTE__COLORPICKER
  576. // idle, over, down, over&down, selected, sel&over, disabled
  577. static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =
  578. {
  579. {
  580. { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050, },
  581. { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060, },
  582. { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
  583. }, {
  584. { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, },
  585. { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, },
  586. { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, },
  587. }, {
  588. { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040, },
  589. { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08, },
  590. { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074, },
  591. }, {
  592. { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024, },
  593. { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030, },
  594. { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
  595. }, {
  596. { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, },
  597. { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060, },
  598. { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
  599. }, {
  600. { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, },
  601. { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060, },
  602. { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
  603. }, {
  604. { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040, },
  605. { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050, },
  606. { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, },
  607. }, {
  608. { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, },
  609. { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, },
  610. { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
  611. }, {
  612. { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, },
  613. { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, },
  614. { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
  615. }, {
  616. { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c, },
  617. { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060, },
  618. { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090, },
  619. }, {
  620. { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050, },
  621. { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060, },
  622. { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090, },
  623. }, {
  624. { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050, },
  625. { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060, },
  626. { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090, },
  627. }, {
  628. { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828, },
  629. { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838, },
  630. { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090, },
  631. },
  632. };
  633. #define STBTE_COLOR_TILEMAP_BACKGROUND 0x000000
  634. #define STBTE_COLOR_TILEMAP_BORDER 0x203060
  635. #define STBTE_COLOR_TILEMAP_HIGHLIGHT 0xffffff
  636. #define STBTE_COLOR_GRID 0x404040
  637. #define STBTE_COLOR_SELECTION_OUTLINE1 0xdfdfdf
  638. #define STBTE_COLOR_SELECTION_OUTLINE2 0x303030
  639. #define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff
  640. #define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000
  641. #ifndef STBTE_LINK_COLOR
  642. #define STBTE_LINK_COLOR(src,sp,dest,dp) 0x5030ff
  643. #endif
  644. #ifndef STBTE_LINK_COLOR_DRAWING
  645. #define STBTE_LINK_COLOR_DRAWING 0xff40ff
  646. #endif
  647. #ifndef STBTE_LINK_COLOR_DISALLOWED
  648. #define STBTE_LINK_COLOR_DISALLOWED 0x602060
  649. #endif
  650. // disabled, selected, down, over
  651. static unsigned char stbte__state_to_index[2][2][2][2] =
  652. {
  653. {
  654. { { STBTE__idle , STBTE__over }, { STBTE__down , STBTE__over_down }, },
  655. { { STBTE__selected, STBTE__selected_over }, { STBTE__down , STBTE__over_down }, },
  656. },{
  657. { { STBTE__disabled, STBTE__disabled }, { STBTE__disabled, STBTE__disabled }, },
  658. { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled }, },
  659. }
  660. };
  661. #define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over]
  662. #define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id))
  663. #define STBTE__FONT_HEIGHT 9
  664. static short stbte__font_offset[95+16];
  665. static short stbte__fontdata[769] =
  666. {
  667. 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,
  668. 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,
  669. 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,
  670. 0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146,
  671. 511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16,
  672. 124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496,
  673. 496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257,
  674. 257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8,
  675. 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,
  676. 127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93,
  677. 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,
  678. 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,
  679. 127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39,
  680. 103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54,
  681. 6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28,
  682. 8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127,
  683. 54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9,
  684. 9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64,
  685. 64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127,
  686. 127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65,
  687. 81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127,
  688. 64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119,
  689. 99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65,
  690. 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,
  691. 127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92,
  692. 24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122,
  693. 64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28,
  694. 124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124,
  695. 68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124,
  696. 64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108,
  697. 68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65,
  698. 65,119,62,8,16,24,12,12,24,24,12,4,
  699. };
  700. typedef struct
  701. {
  702. short id;
  703. unsigned short category_id;
  704. char *category;
  705. unsigned int layermask;
  706. } stbte__tileinfo;
  707. #define MAX_LAYERMASK (1 << (8*sizeof(unsigned int)))
  708. typedef short stbte__tiledata;
  709. #define STBTE__NO_TILE -1
  710. enum
  711. {
  712. STBTE__panel_toolbar,
  713. STBTE__panel_colorpick,
  714. STBTE__panel_info,
  715. STBTE__panel_layers,
  716. STBTE__panel_props,
  717. STBTE__panel_categories,
  718. STBTE__panel_tiles,
  719. STBTE__num_panel,
  720. };
  721. enum
  722. {
  723. STBTE__side_left,
  724. STBTE__side_right,
  725. STBTE__side_top,
  726. STBTE__side_bottom,
  727. };
  728. enum
  729. {
  730. STBTE__tool_select,
  731. STBTE__tool_brush,
  732. STBTE__tool_erase,
  733. STBTE__tool_rect,
  734. STBTE__tool_eyedrop,
  735. STBTE__tool_fill,
  736. STBTE__tool_link,
  737. STBTE__tool_showgrid,
  738. STBTE__tool_showlinks,
  739. STBTE__tool_undo,
  740. STBTE__tool_redo,
  741. // copy/cut/paste aren't included here because they're displayed differently
  742. STBTE__num_tool,
  743. };
  744. // icons are stored in the 0-31 range of ASCII in the font
  745. static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28, };
  746. enum
  747. {
  748. STBTE__propmode_default,
  749. STBTE__propmode_always,
  750. STBTE__propmode_never,
  751. };
  752. enum
  753. {
  754. STBTE__paint,
  755. // from here down does hittesting
  756. STBTE__tick,
  757. STBTE__mousemove,
  758. STBTE__mousewheel,
  759. STBTE__leftdown,
  760. STBTE__leftup,
  761. STBTE__rightdown,
  762. STBTE__rightup,
  763. };
  764. typedef struct
  765. {
  766. int expanded, mode;
  767. int delta_height; // number of rows they've requested for this
  768. int side;
  769. int width,height;
  770. int x0,y0;
  771. } stbte__panel;
  772. typedef struct
  773. {
  774. int x0,y0,x1,y1,color;
  775. } stbte__colorrect;
  776. #define STBTE__MAX_DELAYRECT 256
  777. typedef struct
  778. {
  779. int tool, active_event;
  780. int active_id, hot_id, next_hot_id;
  781. int event;
  782. int mx,my, dx,dy;
  783. int ms_time;
  784. int shift, scrollkey;
  785. int initted;
  786. int side_extended[2];
  787. stbte__colorrect delayrect[STBTE__MAX_DELAYRECT];
  788. int delaycount;
  789. int show_grid, show_links;
  790. int brush_state; // used to decide which kind of erasing
  791. int eyedrop_x, eyedrop_y, eyedrop_last_layer;
  792. int pasting, paste_x, paste_y;
  793. int scrolling, start_x, start_y;
  794. int last_mouse_x, last_mouse_y;
  795. int accum_x, accum_y;
  796. int linking;
  797. int dragging;
  798. int drag_x, drag_y, drag_w, drag_h;
  799. int drag_offx, drag_offy, drag_dest_x, drag_dest_y;
  800. int undoing;
  801. int has_selection, select_x0, select_y0, select_x1, select_y1;
  802. int sx,sy;
  803. int x0,y0,x1,y1, left_width, right_width; // configurable widths
  804. float alert_timer;
  805. const char *alert_msg;
  806. float dt;
  807. stbte__panel panel[STBTE__num_panel];
  808. short copybuffer[STBTE_MAX_COPY][STBTE_MAX_LAYERS];
  809. float copyprops[STBTE_MAX_COPY][STBTE_MAX_PROPERTIES];
  810. #ifdef STBTE_ALLOW_LINK
  811. stbte__link copylinks[STBTE_MAX_COPY];
  812. #endif
  813. int copy_src_x, copy_src_y;
  814. stbte_tilemap *copy_src;
  815. int copy_width,copy_height,has_copy,copy_has_props;
  816. } stbte__ui_t;
  817. // there's only one UI system at a time, so we can globalize this
  818. static stbte__ui_t stbte__ui = { STBTE__tool_brush, 0 };
  819. #define STBTE__INACTIVE() (stbte__ui.active_id == 0)
  820. #define STBTE__IS_ACTIVE(id) (stbte__ui.active_id == (id))
  821. #define STBTE__IS_HOT(id) (stbte__ui.hot_id == (id))
  822. #define STBTE__BUTTON_HEIGHT (STBTE__FONT_HEIGHT + 2 * STBTE__BUTTON_INTERNAL_SPACING)
  823. #define STBTE__BUTTON_INTERNAL_SPACING (2 + (STBTE__FONT_HEIGHT>>4))
  824. typedef struct
  825. {
  826. const char *name;
  827. int locked;
  828. int hidden;
  829. } stbte__layer;
  830. enum
  831. {
  832. STBTE__unlocked,
  833. STBTE__protected,
  834. STBTE__locked,
  835. };
  836. struct stbte_tilemap
  837. {
  838. stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS];
  839. float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROPERTIES];
  840. #ifdef STBTE_ALLOW_LINK
  841. stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
  842. int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
  843. #endif
  844. int max_x, max_y, num_layers;
  845. int spacing_x, spacing_y;
  846. int palette_spacing_x, palette_spacing_y;
  847. int scroll_x,scroll_y;
  848. int cur_category, cur_tile, cur_layer;
  849. char *categories[STBTE_MAX_CATEGORIES];
  850. int num_categories, category_scroll;
  851. stbte__tileinfo *tiles;
  852. int num_tiles, max_tiles, digits;
  853. unsigned char undo_available_valid;
  854. unsigned char undo_available;
  855. unsigned char redo_available;
  856. unsigned char padding;
  857. int cur_palette_count;
  858. int palette_scroll;
  859. int tileinfo_dirty;
  860. stbte__layer layerinfo[STBTE_MAX_LAYERS];
  861. int has_layer_names;
  862. int layername_width;
  863. int layer_scroll;
  864. int propmode;
  865. int solo_layer;
  866. int undo_pos, undo_len, redo_len;
  867. short background_tile;
  868. unsigned char id_in_use[32768>>3];
  869. short *undo_buffer;
  870. };
  871. static char *default_category = "[unassigned]";
  872. static void stbte__init_gui(void)
  873. {
  874. int i,n;
  875. stbte__ui.initted = 1;
  876. // init UI state
  877. stbte__ui.show_links = 1;
  878. for (i=0; i < STBTE__num_panel; ++i) {
  879. stbte__ui.panel[i].expanded = 1; // visible if not autohidden
  880. stbte__ui.panel[i].delta_height = 0;
  881. stbte__ui.panel[i].side = STBTE__side_left;
  882. }
  883. stbte__ui.panel[STBTE__panel_toolbar ].side = STBTE__side_top;
  884. stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right;
  885. if (stbte__ui.left_width == 0)
  886. stbte__ui.left_width = 80;
  887. if (stbte__ui.right_width == 0)
  888. stbte__ui.right_width = 80;
  889. // init font
  890. n=95+16;
  891. for (i=0; i < 95+16; ++i) {
  892. stbte__font_offset[i] = n;
  893. n += stbte__fontdata[i];
  894. }
  895. }
  896. stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles)
  897. {
  898. int i;
  899. stbte_tilemap *tm;
  900. STBTE_ASSERT(map_layers >= 0 && map_layers <= STBTE_MAX_LAYERS);
  901. STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
  902. STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
  903. if (map_x < 0 || map_y < 0 || map_layers < 0 ||
  904. map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y || map_layers > STBTE_MAX_LAYERS)
  905. return NULL;
  906. if (!stbte__ui.initted)
  907. stbte__init_gui();
  908. tm = (stbte_tilemap *) malloc(sizeof(*tm) + sizeof(*tm->tiles) * max_tiles + STBTE_UNDO_BUFFER_BYTES);
  909. if (tm == NULL)
  910. return NULL;
  911. tm->tiles = (stbte__tileinfo *) (tm+1);
  912. tm->undo_buffer = (short *) (tm->tiles + max_tiles);
  913. tm->num_layers = map_layers;
  914. tm->max_x = map_x;
  915. tm->max_y = map_y;
  916. tm->spacing_x = spacing_x;
  917. tm->spacing_y = spacing_y;
  918. tm->scroll_x = 0;
  919. tm->scroll_y = 0;
  920. tm->palette_scroll = 0;
  921. tm->palette_spacing_x = spacing_x+1;
  922. tm->palette_spacing_y = spacing_y+1;
  923. tm->cur_category = -1;
  924. tm->cur_tile = 0;
  925. tm->solo_layer = -1;
  926. tm->undo_len = 0;
  927. tm->redo_len = 0;
  928. tm->undo_pos = 0;
  929. tm->category_scroll = 0;
  930. tm->layer_scroll = 0;
  931. tm->propmode = 0;
  932. tm->has_layer_names = 0;
  933. tm->layername_width = 0;
  934. tm->undo_available_valid = 0;
  935. for (i=0; i < tm->num_layers; ++i) {
  936. tm->layerinfo[i].hidden = 0;
  937. tm->layerinfo[i].locked = STBTE__unlocked;
  938. tm->layerinfo[i].name = 0;
  939. }
  940. tm->background_tile = STBTE__NO_TILE;
  941. stbte_clear_map(tm);
  942. tm->max_tiles = max_tiles;
  943. tm->num_tiles = 0;
  944. for (i=0; i < 32768/8; ++i)
  945. tm->id_in_use[i] = 0;
  946. tm->tileinfo_dirty = 1;
  947. return tm;
  948. }
  949. void stbte_set_background_tile(stbte_tilemap *tm, short id)
  950. {
  951. int i;
  952. STBTE_ASSERT(id >= -1 && id < 32768);
  953. if (id >= 32768 || id < -1)
  954. return;
  955. for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i)
  956. if (tm->data[0][i][0] == -1)
  957. tm->data[0][i][0] = id;
  958. tm->background_tile = id;
  959. }
  960. void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y)
  961. {
  962. tm->spacing_x = spacing_x;
  963. tm->spacing_y = spacing_y;
  964. tm->palette_spacing_x = palette_spacing_x;
  965. tm->palette_spacing_y = palette_spacing_y;
  966. }
  967. void stbte_set_sidewidths(int left, int right)
  968. {
  969. stbte__ui.left_width = left;
  970. stbte__ui.right_width = right;
  971. }
  972. void stbte_set_display(int x0, int y0, int x1, int y1)
  973. {
  974. stbte__ui.x0 = x0;
  975. stbte__ui.y0 = y0;
  976. stbte__ui.x1 = x1;
  977. stbte__ui.y1 = y1;
  978. }
  979. void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category_c)
  980. {
  981. char *category = (char *) category_c;
  982. STBTE_ASSERT(id < 32768);
  983. STBTE_ASSERT(tm->num_tiles < tm->max_tiles);
  984. STBTE_ASSERT((tm->id_in_use[id>>3]&(1<<(id&7))) == 0);
  985. if (id >= 32768 || tm->num_tiles >= tm->max_tiles || (tm->id_in_use[id>>3]&(1<<(id&7))))
  986. return;
  987. if (category == NULL)
  988. category = (char*) default_category;
  989. tm->id_in_use[id>>3] |= 1 << (id&7);
  990. tm->tiles[tm->num_tiles].category = category;
  991. tm->tiles[tm->num_tiles].id = id;
  992. tm->tiles[tm->num_tiles].layermask = layermask;
  993. ++tm->num_tiles;
  994. tm->tileinfo_dirty = 1;
  995. }
  996. static int stbte__text_width(const char *str);
  997. void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername)
  998. {
  999. STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
  1000. if (layer >= 0 && layer < tm->num_layers) {
  1001. int width;
  1002. tm->layerinfo[layer].name = layername;
  1003. tm->has_layer_names = 1;
  1004. width = stbte__text_width(layername);
  1005. tm->layername_width = (width > tm->layername_width ? width : tm->layername_width);
  1006. }
  1007. }
  1008. void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y)
  1009. {
  1010. *max_x = tm->max_x;
  1011. *max_y = tm->max_y;
  1012. }
  1013. short* stbte_get_tile(stbte_tilemap *tm, int x, int y)
  1014. {
  1015. STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
  1016. if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
  1017. return NULL;
  1018. return tm->data[y][x];
  1019. }
  1020. float *stbte_get_properties(stbte_tilemap *tm, int x, int y)
  1021. {
  1022. STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
  1023. if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
  1024. return NULL;
  1025. return tm->props[y][x];
  1026. }
  1027. void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty)
  1028. {
  1029. int gx=-1,gy=-1;
  1030. STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
  1031. #ifdef STBTE_ALLOW_LINK
  1032. if (x >= 0 && x < STBTE_MAX_TILEMAP_X && y >= 0 && y < STBTE_MAX_TILEMAP_Y) {
  1033. gx = tm->link[y][x].x;
  1034. gy = tm->link[y][x].y;
  1035. if (gx >= 0)
  1036. if (!STBTE_ALLOW_LINK(tm->data[y][x], tm->props[y][x], tm->data[gy][gx], tm->props[gy][gx]))
  1037. gx = gy = -1;
  1038. }
  1039. #endif
  1040. *destx = gx;
  1041. *desty = gy;
  1042. }
  1043. void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val)
  1044. {
  1045. tm->props[y][x][n] = val;
  1046. }
  1047. static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode);
  1048. enum
  1049. {
  1050. STBTE__undo_none,
  1051. STBTE__undo_record,
  1052. STBTE__undo_block,
  1053. };
  1054. void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty)
  1055. {
  1056. #ifdef STBTE_ALLOW_LINK
  1057. stbte__set_link(tm, x, y, destx, desty, STBTE__undo_none);
  1058. #else
  1059. STBTE_ASSERT(0);
  1060. #endif
  1061. }
  1062. // returns an array of map_layers shorts. each short is either
  1063. // one of the tile_id values from define_tile, or STBTE_EMPTY
  1064. void stbte_set_dimensions(stbte_tilemap *tm, int map_x, int map_y)
  1065. {
  1066. STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
  1067. STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
  1068. if (map_x < 0 || map_y < 0 || map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y)
  1069. return;
  1070. tm->max_x = map_x;
  1071. tm->max_y = map_y;
  1072. }
  1073. void stbte_clear_map(stbte_tilemap *tm)
  1074. {
  1075. int i,j;
  1076. for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) {
  1077. tm->data[0][i][0] = tm->background_tile;
  1078. for (j=1; j < tm->num_layers; ++j)
  1079. tm->data[0][i][j] = STBTE__NO_TILE;
  1080. for (j=0; j < STBTE_MAX_PROPERTIES; ++j)
  1081. tm->props[0][i][j] = 0;
  1082. #ifdef STBTE_ALLOW_LINK
  1083. tm->link[0][i].x = -1;
  1084. tm->link[0][i].y = -1;
  1085. tm->linkcount[0][i] = 0;
  1086. #endif
  1087. }
  1088. }
  1089. void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile)
  1090. {
  1091. STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
  1092. STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
  1093. STBTE_ASSERT(tile >= -1 && tile < 32768);
  1094. if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
  1095. return;
  1096. if (layer < 0 || layer >= tm->num_layers || tile < -1)
  1097. return;
  1098. tm->data[y][x][layer] = tile;
  1099. }
  1100. static void stbte__choose_category(stbte_tilemap *tm, int category)
  1101. {
  1102. int i,n=0;
  1103. tm->cur_category = category;
  1104. for (i=0; i < tm->num_tiles; ++i)
  1105. if (tm->tiles[i].category_id == category || category == -1)
  1106. ++n;
  1107. tm->cur_palette_count = n;
  1108. tm->palette_scroll = 0;
  1109. }
  1110. static int stbte__strequal(char *p, char *q)
  1111. {
  1112. while (*p)
  1113. if (*p++ != *q++) return 0;
  1114. return *q == 0;
  1115. }
  1116. static void stbte__compute_tileinfo(stbte_tilemap *tm)
  1117. {
  1118. int i,j,n=0;
  1119. tm->num_categories=0;
  1120. for (i=0; i < tm->num_tiles; ++i) {
  1121. stbte__tileinfo *t = &tm->tiles[i];
  1122. // find category
  1123. for (j=0; j < tm->num_categories; ++j)
  1124. if (stbte__strequal(t->category, tm->categories[j]))
  1125. goto found;
  1126. tm->categories[j] = t->category;
  1127. ++tm->num_categories;
  1128. found:
  1129. t->category_id = (unsigned short) j;
  1130. }
  1131. // currently number of categories can never decrease because you
  1132. // can't remove tile definitions, but let's get it right anyway
  1133. if (tm->cur_category > tm->num_categories) {
  1134. tm->cur_category = -1;
  1135. }
  1136. stbte__choose_category(tm, tm->cur_category);
  1137. tm->tileinfo_dirty = 0;
  1138. }
  1139. static void stbte__prepare_tileinfo(stbte_tilemap *tm)
  1140. {
  1141. if (tm->tileinfo_dirty)
  1142. stbte__compute_tileinfo(tm);
  1143. }
  1144. /////////////////////// undo system ////////////////////////
  1145. // the undo system works by storing "commands" into a buffer, and
  1146. // then playing back those commands. undo and redo have to store
  1147. // the commands in different order.
  1148. //
  1149. // the commands are:
  1150. //
  1151. // 1) end_of_undo_record
  1152. // -1:short
  1153. //
  1154. // 2) end_of_redo_record
  1155. // -2:short
  1156. //
  1157. // 3) tile update
  1158. // tile_id:short (-1..32767)
  1159. // x_coord:short
  1160. // y_coord:short
  1161. // layer:short (0..31)
  1162. //
  1163. // 4) property update (also used for links)
  1164. // value_hi:short
  1165. // value_lo:short
  1166. // y_coord:short
  1167. // x_coord:short
  1168. // property:short (256+prop#)
  1169. //
  1170. // Since we use a circular buffer, we might overwrite the undo storage.
  1171. // To detect this, before playing back commands we scan back and see
  1172. // if we see an end_of_undo_record before hitting the relevant boundary,
  1173. // it's wholly contained.
  1174. //
  1175. // When we read back through, we see them in reverse order, so
  1176. // we'll see the layer number or property number first
  1177. //
  1178. // To be clearer about the circular buffer, there are two cases:
  1179. // 1. a single record is larger than the whole buffer.
  1180. // this is caught because the end_of_undo_record will
  1181. // get overwritten.
  1182. // 2. multiple records written are larger than the whole
  1183. // buffer, so some of them have been overwritten by
  1184. // the later ones. this is handled by explicitly tracking
  1185. // the undo length; we never try to parse the data that
  1186. // got overwritten
  1187. // given two points, compute the length between them
  1188. #define stbte__wrap(pos) ((pos) & (STBTE__UNDO_BUFFER_COUNT-1))
  1189. #define STBTE__undo_record -2
  1190. #define STBTE__redo_record -3
  1191. #define STBTE__undo_junk -4 // this is written underneath the undo pointer, never used
  1192. static void stbte__write_undo(stbte_tilemap *tm, short value)
  1193. {
  1194. int pos = tm->undo_pos;
  1195. tm->undo_buffer[pos] = value;
  1196. tm->undo_pos = stbte__wrap(pos+1);
  1197. tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2);
  1198. tm->redo_len -= (tm->redo_len > 0);
  1199. tm->undo_available_valid = 0;
  1200. }
  1201. static void stbte__write_redo(stbte_tilemap *tm, short value)
  1202. {
  1203. int pos = tm->undo_pos;
  1204. tm->undo_buffer[pos] = value;
  1205. tm->undo_pos = stbte__wrap(pos-1);
  1206. tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2);
  1207. tm->undo_len -= (tm->undo_len > 0);
  1208. tm->undo_available_valid = 0;
  1209. }
  1210. static void stbte__begin_undo(stbte_tilemap *tm)
  1211. {
  1212. tm->redo_len = 0;
  1213. stbte__write_undo(tm, STBTE__undo_record);
  1214. stbte__ui.undoing = 1;
  1215. stbte__ui.alert_msg = 0; // clear alert if they start doing something
  1216. }
  1217. static void stbte__end_undo(stbte_tilemap *tm)
  1218. {
  1219. if (stbte__ui.undoing) {
  1220. // check if anything got written
  1221. int pos = stbte__wrap(tm->undo_pos-1);
  1222. if (tm->undo_buffer[pos] == STBTE__undo_record) {
  1223. // empty undo record, move back
  1224. tm->undo_pos = pos;
  1225. STBTE_ASSERT(tm->undo_len > 0);
  1226. tm->undo_len -= 1;
  1227. }
  1228. tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
  1229. // otherwise do nothing
  1230. stbte__ui.undoing = 0;
  1231. }
  1232. }
  1233. static void stbte__undo_record(stbte_tilemap *tm, int x, int y, int i, int v)
  1234. {
  1235. STBTE_ASSERT(stbte__ui.undoing);
  1236. if (stbte__ui.undoing) {
  1237. stbte__write_undo(tm, v);
  1238. stbte__write_undo(tm, x);
  1239. stbte__write_undo(tm, y);
  1240. stbte__write_undo(tm, i);
  1241. }
  1242. }
  1243. static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v)
  1244. {
  1245. stbte__write_redo(tm, v);
  1246. stbte__write_redo(tm, x);
  1247. stbte__write_redo(tm, y);
  1248. stbte__write_redo(tm, i);
  1249. }
  1250. static float stbte__extract_float(short s0, short s1)
  1251. {
  1252. union { float f; short s[2]; } converter;
  1253. converter.s[0] = s0;
  1254. converter.s[1] = s1;
  1255. return converter.f;
  1256. }
  1257. static short stbte__extract_short(float f, int slot)
  1258. {
  1259. union { float f; short s[2]; } converter;
  1260. converter.f = f;
  1261. return converter.s[slot];
  1262. }
  1263. static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
  1264. {
  1265. STBTE_ASSERT(stbte__ui.undoing);
  1266. if (stbte__ui.undoing) {
  1267. stbte__write_undo(tm, s1);
  1268. stbte__write_undo(tm, s0);
  1269. stbte__write_undo(tm, x);
  1270. stbte__write_undo(tm, y);
  1271. stbte__write_undo(tm, 256+i);
  1272. }
  1273. }
  1274. static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f)
  1275. {
  1276. stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1));
  1277. }
  1278. static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
  1279. {
  1280. stbte__write_redo(tm, s1);
  1281. stbte__write_redo(tm, s0);
  1282. stbte__write_redo(tm, x);
  1283. stbte__write_redo(tm, y);
  1284. stbte__write_redo(tm, 256+i);
  1285. }
  1286. static int stbte__undo_find_end(stbte_tilemap *tm)
  1287. {
  1288. // first scan through for the end record
  1289. int i, pos = stbte__wrap(tm->undo_pos-1);
  1290. for (i=0; i < tm->undo_len;) {
  1291. STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
  1292. if (tm->undo_buffer[pos] == STBTE__undo_record)
  1293. break;
  1294. if (tm->undo_buffer[pos] >= 255)
  1295. pos = stbte__wrap(pos-5), i += 5;
  1296. else
  1297. pos = stbte__wrap(pos-4), i += 4;
  1298. }
  1299. if (i >= tm->undo_len)
  1300. return -1;
  1301. return pos;
  1302. }
  1303. static void stbte__undo(stbte_tilemap *tm)
  1304. {
  1305. int i, pos, endpos;
  1306. endpos = stbte__undo_find_end(tm);
  1307. if (endpos < 0)
  1308. return;
  1309. // we found a complete undo record
  1310. pos = stbte__wrap(tm->undo_pos-1);
  1311. // start a redo record
  1312. stbte__write_redo(tm, STBTE__redo_record);
  1313. // so now go back through undo and apply in reverse
  1314. // order, and copy it to redo
  1315. for (i=0; endpos != pos; i += 4) {
  1316. int x,y,n,v;
  1317. // get the undo entry
  1318. n = tm->undo_buffer[pos];
  1319. y = tm->undo_buffer[stbte__wrap(pos-1)];
  1320. x = tm->undo_buffer[stbte__wrap(pos-2)];
  1321. v = tm->undo_buffer[stbte__wrap(pos-3)];
  1322. if (n >= 255) {
  1323. short s0=0,s1=0;
  1324. int v2 = tm->undo_buffer[stbte__wrap(pos-4)];
  1325. pos = stbte__wrap(pos-5);
  1326. if (n > 255) {
  1327. float vf = stbte__extract_float(v, v2);
  1328. s0 = stbte__extract_short(tm->props[y][x][n-256], 0);
  1329. s1 = stbte__extract_short(tm->props[y][x][n-256], 1);
  1330. tm->props[y][x][n-256] = vf;
  1331. } else {
  1332. #ifdef STBTE_ALLOW_LINK
  1333. s0 = tm->link[y][x].x;
  1334. s1 = tm->link[y][x].y;
  1335. stbte__set_link(tm, x,y, v, v2, STBTE__undo_none);
  1336. #endif
  1337. }
  1338. // write the redo entry
  1339. stbte__redo_record_prop(tm, x, y, n-256, s0,s1);
  1340. // apply the undo entry
  1341. } else {
  1342. pos = stbte__wrap(pos-4);
  1343. // write the redo entry
  1344. stbte__redo_record(tm, x, y, n, tm->data[y][x][n]);
  1345. // apply the undo entry
  1346. tm->data[y][x][n] = (short) v;
  1347. }
  1348. }
  1349. // overwrite undo record with junk
  1350. tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
  1351. }
  1352. static int stbte__redo_find_end(stbte_tilemap *tm)
  1353. {
  1354. // first scan through for the end record
  1355. int i, pos = stbte__wrap(tm->undo_pos+1);
  1356. for (i=0; i < tm->redo_len;) {
  1357. STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
  1358. if (tm->undo_buffer[pos] == STBTE__redo_record)
  1359. break;
  1360. if (tm->undo_buffer[pos] >= 255)
  1361. pos = stbte__wrap(pos+5), i += 5;
  1362. else
  1363. pos = stbte__wrap(pos+4), i += 4;
  1364. }
  1365. if (i >= tm->redo_len)
  1366. return -1; // this should only ever happen if redo buffer is empty
  1367. return pos;
  1368. }
  1369. static void stbte__redo(stbte_tilemap *tm)
  1370. {
  1371. // first scan through for the end record
  1372. int i, pos, endpos;
  1373. endpos = stbte__redo_find_end(tm);
  1374. if (endpos < 0)
  1375. return;
  1376. // we found a complete redo record
  1377. pos = stbte__wrap(tm->undo_pos+1);
  1378. // start an undo record
  1379. stbte__write_undo(tm, STBTE__undo_record);
  1380. for (i=0; pos != endpos; i += 4) {
  1381. int x,y,n,v;
  1382. n = tm->undo_buffer[pos];
  1383. y = tm->undo_buffer[stbte__wrap(pos+1)];
  1384. x = tm->undo_buffer[stbte__wrap(pos+2)];
  1385. v = tm->undo_buffer[stbte__wrap(pos+3)];
  1386. if (n >= 255) {
  1387. int v2 = tm->undo_buffer[stbte__wrap(pos+4)];
  1388. short s0=0,s1=0;
  1389. pos = stbte__wrap(pos+5);
  1390. if (n > 255) {
  1391. float vf = stbte__extract_float(v, v2);
  1392. s0 = stbte__extract_short(tm->props[y][x][n-256],0);
  1393. s1 = stbte__extract_short(tm->props[y][x][n-256],1);
  1394. tm->props[y][x][n-256] = vf;
  1395. } else {
  1396. #ifdef STBTE_ALLOW_LINK
  1397. s0 = tm->link[y][x].x;
  1398. s1 = tm->link[y][x].y;
  1399. stbte__set_link(tm, x,y,v,v2, STBTE__undo_none);
  1400. #endif
  1401. }
  1402. // don't use stbte__undo_record_prop because it's guarded
  1403. stbte__write_undo(tm, s1);
  1404. stbte__write_undo(tm, s0);
  1405. stbte__write_undo(tm, x);
  1406. stbte__write_undo(tm, y);
  1407. stbte__write_undo(tm, n);
  1408. } else {
  1409. pos = stbte__wrap(pos+4);
  1410. // don't use stbte__undo_record because it's guarded
  1411. stbte__write_undo(tm, tm->data[y][x][n]);
  1412. stbte__write_undo(tm, x);
  1413. stbte__write_undo(tm, y);
  1414. stbte__write_undo(tm, n);
  1415. tm->data[y][x][n] = (short) v;
  1416. }
  1417. }
  1418. tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
  1419. }
  1420. // because detecting that undo is available
  1421. static void stbte__recompute_undo_available(stbte_tilemap *tm)
  1422. {
  1423. tm->undo_available = (stbte__undo_find_end(tm) >= 0);
  1424. tm->redo_available = (stbte__redo_find_end(tm) >= 0);
  1425. }
  1426. static int stbte__undo_available(stbte_tilemap *tm)
  1427. {
  1428. if (!tm->undo_available_valid)
  1429. stbte__recompute_undo_available(tm);
  1430. return tm->undo_available;
  1431. }
  1432. static int stbte__redo_available(stbte_tilemap *tm)
  1433. {
  1434. if (!tm->undo_available_valid)
  1435. stbte__recompute_undo_available(tm);
  1436. return tm->redo_available;
  1437. }
  1438. ///////////////////////////////////////////////////////////////////////////////////////////////////
  1439. #ifdef STBTE_ALLOW_LINK
  1440. static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode)
  1441. {
  1442. stbte__link *a;
  1443. STBTE_ASSERT(src_x >= 0 && src_x < STBTE_MAX_TILEMAP_X && src_y >= 0 && src_y < STBTE_MAX_TILEMAP_Y);
  1444. a = &tm->link[src_y][src_x];
  1445. // check if it's a do nothing
  1446. if (a->x == dest_x && a->y == dest_y)
  1447. return;
  1448. if (undo_mode != STBTE__undo_none ) {
  1449. if (undo_mode == STBTE__undo_block) stbte__begin_undo(tm);
  1450. stbte__undo_record_prop(tm, src_x, src_y, -1, a->x, a->y);
  1451. if (undo_mode == STBTE__undo_block) stbte__end_undo(tm);
  1452. }
  1453. // check if there's an existing link
  1454. if (a->x >= 0) {
  1455. // decrement existing link refcount
  1456. STBTE_ASSERT(tm->linkcount[a->y][a->x] > 0);
  1457. --tm->linkcount[a->y][a->x];
  1458. }
  1459. // increment new dest
  1460. if (dest_x >= 0) {
  1461. ++tm->linkcount[dest_y][dest_x];
  1462. }
  1463. a->x = dest_x;
  1464. a->y = dest_y;
  1465. }
  1466. #endif
  1467. static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color)
  1468. {
  1469. STBTE_DRAW_RECT(x0,y0,x1,y1, color);
  1470. }
  1471. static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color)
  1472. {
  1473. int temp;
  1474. if (x1 < x0) temp=x0,x0=x1,x1=temp;
  1475. if (y1 < y0) temp=y0,y0=y1,y1=temp;
  1476. stbte__draw_rect(x0,y0,x1+1,y1+1,color);
  1477. }
  1478. static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color)
  1479. {
  1480. stbte__draw_line(x0,y0,x0,y1, color);
  1481. stbte__draw_line(x0,y1,x1,y1, color);
  1482. }
  1483. static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color)
  1484. {
  1485. stbte__draw_rect(x0,y0,x1-1,y0+1,color);
  1486. stbte__draw_rect(x1-1,y0,x1,y1-1,color);
  1487. stbte__draw_rect(x0+1,y1-1,x1,y1,color);
  1488. stbte__draw_rect(x0,y0+1,x0+1,y1,color);
  1489. }
  1490. static void stbte__draw_halfframe(int x0, int y0, int x1, int y1, unsigned int color)
  1491. {
  1492. stbte__draw_rect(x0,y0,x1,y0+1,color);
  1493. stbte__draw_rect(x0,y0+1,x0+1,y1,color);
  1494. }
  1495. static int stbte__get_char_width(int ch)
  1496. {
  1497. return stbte__fontdata[ch-16];
  1498. }
  1499. static short *stbte__get_char_bitmap(int ch)
  1500. {
  1501. return stbte__fontdata + stbte__font_offset[ch-16];
  1502. }
  1503. static void stbte__draw_bitmask_as_columns(int x, int y, short bitmask, int color)
  1504. {
  1505. int start_i = -1, i=0;
  1506. while (bitmask) {
  1507. if (bitmask & (1<<i)) {
  1508. if (start_i < 0)
  1509. start_i = i;
  1510. } else if (start_i >= 0) {
  1511. stbte__draw_rect(x, y+start_i, x+1, y+i, color);
  1512. start_i = -1;
  1513. bitmask &= ~((1<<i)-1); // clear all the old bits; we don't clear them as we go to save code
  1514. }
  1515. ++i;
  1516. }
  1517. }
  1518. static void stbte__draw_bitmap(int x, int y, int w, short *bitmap, int color)
  1519. {
  1520. int i;
  1521. for (i=0; i < w; ++i)
  1522. stbte__draw_bitmask_as_columns(x+i, y, *bitmap++, color);
  1523. }
  1524. static void stbte__draw_text_core(int x, int y, const char *str, int w, int color, int digitspace)
  1525. {
  1526. int x_end = x+w;
  1527. while (*str) {
  1528. int c = *str++;
  1529. int cw = stbte__get_char_width(c);
  1530. if (x + cw > x_end)
  1531. break;
  1532. stbte__draw_bitmap(x, y, cw, stbte__get_char_bitmap(c), color);
  1533. if (digitspace && c == ' ')
  1534. cw = stbte__get_char_width('0');
  1535. x += cw+1;
  1536. }
  1537. }
  1538. static void stbte__draw_text(int x, int y, const char *str, int w, int color)
  1539. {
  1540. stbte__draw_text_core(x,y,str,w,color,0);
  1541. }
  1542. static int stbte__text_width(const char *str)
  1543. {
  1544. int x = 0;
  1545. while (*str) {
  1546. int c = *str++;
  1547. int cw = stbte__get_char_width(c);
  1548. x += cw+1;
  1549. }
  1550. return x;
  1551. }
  1552. static void stbte__draw_frame_delayed(int x0, int y0, int x1, int y1, int color)
  1553. {
  1554. if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) {
  1555. stbte__colorrect r = { x0,y0,x1,y1,color };
  1556. stbte__ui.delayrect[stbte__ui.delaycount++] = r;
  1557. }
  1558. }
  1559. static void stbte__flush_delay(void)
  1560. {
  1561. stbte__colorrect *r;
  1562. int i;
  1563. r = stbte__ui.delayrect;
  1564. for (i=0; i < stbte__ui.delaycount; ++i,++r)
  1565. stbte__draw_frame(r->x0,r->y0,r->x1,r->y1,r->color);
  1566. stbte__ui.delaycount = 0;
  1567. }
  1568. static void stbte__activate(int id)
  1569. {
  1570. stbte__ui.active_id = id;
  1571. stbte__ui.active_event = stbte__ui.event;
  1572. stbte__ui.accum_x = 0;
  1573. stbte__ui.accum_y = 0;
  1574. }
  1575. static int stbte__hittest(int x0, int y0, int x1, int y1, int id)
  1576. {
  1577. int over = stbte__ui.mx >= x0 && stbte__ui.my >= y0
  1578. && stbte__ui.mx < x1 && stbte__ui.my < y1;
  1579. if (over && stbte__ui.event >= STBTE__tick)
  1580. stbte__ui.next_hot_id = id;
  1581. return over;
  1582. }
  1583. static int stbte__button_core(int id)
  1584. {
  1585. switch (stbte__ui.event) {
  1586. case STBTE__leftdown:
  1587. if (stbte__ui.hot_id == id && STBTE__INACTIVE())
  1588. stbte__activate(id);
  1589. break;
  1590. case STBTE__leftup:
  1591. if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) {
  1592. stbte__activate(0);
  1593. return 1;
  1594. }
  1595. break;
  1596. case STBTE__rightdown:
  1597. if (stbte__ui.hot_id == id && STBTE__INACTIVE())
  1598. stbte__activate(id);
  1599. break;
  1600. case STBTE__rightup:
  1601. if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) {
  1602. stbte__activate(0);
  1603. return -1;
  1604. }
  1605. break;
  1606. }
  1607. return 0;
  1608. }
  1609. static void stbte__draw_box(int x0, int y0, int x1, int y1, int colormode, int colorindex)
  1610. {
  1611. stbte__draw_rect (x0,y0,x1,y1, stbte__color_table[colormode][STBTE__base ][colorindex]);
  1612. stbte__draw_frame(x0,y0,x1,y1, stbte__color_table[colormode][STBTE__outline][colorindex]);
  1613. }
  1614. static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int xoff, int yoff, int colormode, int colorindex)
  1615. {
  1616. stbte__draw_box(x0,y0,x1,y1,colormode,colorindex);
  1617. stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]);
  1618. }
  1619. static int stbte__button(int colormode, char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled)
  1620. {
  1621. int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
  1622. int s = STBTE__BUTTON_INTERNAL_SPACING;
  1623. int over = !disabled && stbte__hittest(x0,y0,x1,y1,id);
  1624. if (stbte__ui.event == STBTE__paint)
  1625. stbte__draw_textbox(x0,y0,x1,y1, label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
  1626. if (disabled)
  1627. return 0;
  1628. return (stbte__button_core(id) == 1);
  1629. }
  1630. static int stbte__button_icon(int colormode, char ch, int x, int y, int width, int id, int toggled, int disabled)
  1631. {
  1632. int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
  1633. int s = STBTE__BUTTON_INTERNAL_SPACING;
  1634. int over = stbte__hittest(x0,y0,x1,y1,id);
  1635. if (stbte__ui.event == STBTE__paint) {
  1636. char label[2] = { ch, 0 };
  1637. int pad = (9 - stbte__get_char_width(ch))/2;
  1638. stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
  1639. }
  1640. if (disabled)
  1641. return 0;
  1642. return (stbte__button_core(id) == 1);
  1643. }
  1644. static int stbte__minibutton(int colormode, int x, int y, int ch, int id)
  1645. {
  1646. int x0 = x, y0 = y, x1 = x+8, y1 = y+7;
  1647. int over = stbte__hittest(x0,y0,x1,y1,id);
  1648. if (stbte__ui.event == STBTE__paint) {
  1649. char str[2] = { (char)ch, 0 };
  1650. stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0));
  1651. }
  1652. return stbte__button_core(id);
  1653. }
  1654. static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode)
  1655. {
  1656. int x0 = x, y0 = y, x1 = x+10, y1 = y+11;
  1657. int over = !disabled && stbte__hittest(x0,y0,x1,y1,id);
  1658. if (stbte__ui.event == STBTE__paint) {
  1659. char str[2] = { (char)ch, 0 };
  1660. int off = (9-stbte__get_char_width(ch))/2;
  1661. stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
  1662. }
  1663. if (disabled)
  1664. return 0;
  1665. return stbte__button_core(id);
  1666. }
  1667. static int stbte__microbutton(int x, int y, int size, int id, int colormode)
  1668. {
  1669. int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
  1670. int over = stbte__hittest(x0,y0,x1,y1,id);
  1671. if (stbte__ui.event == STBTE__paint) {
  1672. stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0));
  1673. }
  1674. return stbte__button_core(id);
  1675. }
  1676. static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos)
  1677. {
  1678. int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
  1679. int over = stbte__hittest(x0,y0,x1,y1,id);
  1680. switch (stbte__ui.event) {
  1681. case STBTE__paint:
  1682. stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0));
  1683. break;
  1684. case STBTE__leftdown:
  1685. if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
  1686. stbte__activate(id);
  1687. stbte__ui.sx = stbte__ui.mx - *pos;
  1688. }
  1689. break;
  1690. case STBTE__mousemove:
  1691. if (STBTE__IS_ACTIVE(id) && stbte__ui.active_event == STBTE__leftdown) {
  1692. *pos = stbte__ui.mx - stbte__ui.sx;
  1693. }
  1694. break;
  1695. case STBTE__leftup:
  1696. if (STBTE__IS_ACTIVE(id))
  1697. stbte__activate(0);
  1698. break;
  1699. default:
  1700. return stbte__button_core(id);
  1701. }
  1702. return 0;
  1703. }
  1704. static int stbte__category_button(char *label, int x, int y, int width, int id, int toggled)
  1705. {
  1706. int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
  1707. int s = STBTE__BUTTON_INTERNAL_SPACING;
  1708. int over = stbte__hittest(x0,y0,x1,y1,id);
  1709. if (stbte__ui.event == STBTE__paint)
  1710. stbte__draw_textbox(x0,y0,x1,y1, label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled));
  1711. return (stbte__button_core(id) == 1);
  1712. }
  1713. enum
  1714. {
  1715. STBTE__none,
  1716. STBTE__begin,
  1717. STBTE__end,
  1718. STBTE__change,
  1719. };
  1720. // returns -1 if value changes, 1 at end of drag
  1721. static int stbte__slider(int x0, int w, int y, int range, int *value, int id)
  1722. {
  1723. int x1 = x0+w;
  1724. int pos = *value * w / (range+1);
  1725. int over = stbte__hittest(x0,y-2,x1,y+3,id);
  1726. int event_mouse_move = STBTE__change;
  1727. switch (stbte__ui.event) {
  1728. case STBTE__paint:
  1729. stbte__draw_rect(x0,y,x1,y+1, 0x808080);
  1730. stbte__draw_rect(x0+pos-1,y-1,x0+pos+2,y+2, 0xffffff);
  1731. break;
  1732. case STBTE__leftdown:
  1733. if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
  1734. stbte__activate(id);
  1735. event_mouse_move = STBTE__begin;
  1736. }
  1737. // fall through
  1738. case STBTE__mousemove:
  1739. if (STBTE__IS_ACTIVE(id)) {
  1740. int v = (stbte__ui.mx-x0)*(range+1)/w;
  1741. if (v < 0) v = 0; else if (v > range) v = range;
  1742. *value = v;
  1743. return event_mouse_move;
  1744. }
  1745. break;
  1746. case STBTE__leftup:
  1747. if (STBTE__IS_ACTIVE(id)) {
  1748. stbte__activate(0);
  1749. return STBTE__end;
  1750. }
  1751. break;
  1752. }
  1753. return STBTE__none;
  1754. }
  1755. static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, char *fmt, float *value, int colormode, int id)
  1756. {
  1757. int x1 = x0+w;
  1758. int y1 = y0+11;
  1759. int over = stbte__hittest(x0,y0,x1,y1,id);
  1760. switch (stbte__ui.event) {
  1761. case STBTE__paint: {
  1762. char text[32];
  1763. sprintf(text, fmt ? fmt : "%6.2f", *value);
  1764. stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0));
  1765. break;
  1766. }
  1767. case STBTE__leftdown:
  1768. case STBTE__rightdown:
  1769. if (STBTE__IS_HOT(id) && STBTE__INACTIVE())
  1770. stbte__activate(id);
  1771. return STBTE__begin;
  1772. break;
  1773. case STBTE__leftup:
  1774. case STBTE__rightup:
  1775. if (STBTE__IS_ACTIVE(id)) {
  1776. stbte__activate(0);
  1777. return STBTE__end;
  1778. }
  1779. break;
  1780. case STBTE__mousemove:
  1781. if (STBTE__IS_ACTIVE(id)) {
  1782. float v = *value, delta;
  1783. int ax = stbte__ui.accum_x/STBTE_FLOAT_CONTROL_GRANULARITY;
  1784. int ay = stbte__ui.accum_y/STBTE_FLOAT_CONTROL_GRANULARITY;
  1785. stbte__ui.accum_x -= ax*STBTE_FLOAT_CONTROL_GRANULARITY;
  1786. stbte__ui.accum_y -= ay*STBTE_FLOAT_CONTROL_GRANULARITY;
  1787. if (stbte__ui.shift) {
  1788. if (stbte__ui.active_event == STBTE__leftdown)
  1789. delta = ax * 16.0f + ay;
  1790. else
  1791. delta = ax / 16.0f + ay / 256.0f;
  1792. } else {
  1793. if (stbte__ui.active_event == STBTE__leftdown)
  1794. delta = ax*10.0f + ay;
  1795. else
  1796. delta = ax * 0.1f + ay * 0.01f;
  1797. }
  1798. v += delta * scale;
  1799. if (v < minv) v = minv;
  1800. if (v > maxv) v = maxv;
  1801. *value = v;
  1802. return STBTE__change;
  1803. }
  1804. break;
  1805. }
  1806. return STBTE__none;
  1807. }
  1808. static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id)
  1809. {
  1810. int over;
  1811. int thumbpos;
  1812. if (v1 - v0 <= num_vis)
  1813. return;
  1814. // generate thumbpos from numvis
  1815. thumbpos = y0+2 + (y1-y0-4) * *val / (v1 - v0 - num_vis);
  1816. if (thumbpos < y0) thumbpos = y0;
  1817. if (thumbpos >= y1) thumbpos = y1;
  1818. over = stbte__hittest(x-1,y0,x+2,y1,id);
  1819. switch (stbte__ui.event) {
  1820. case STBTE__paint:
  1821. stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]);
  1822. stbte__draw_box(x-1,thumbpos-3,x+2,thumbpos+4, STBTE__cscrollbar, STBTE__INDEX_FOR_ID(id,0,0));
  1823. break;
  1824. case STBTE__leftdown:
  1825. if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
  1826. // check if it's over the thumb
  1827. stbte__activate(id);
  1828. *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0);
  1829. }
  1830. break;
  1831. case STBTE__mousemove:
  1832. if (STBTE__IS_ACTIVE(id) && stbte__ui.mx >= x-15 && stbte__ui.mx <= x+15)
  1833. *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0);
  1834. break;
  1835. case STBTE__leftup:
  1836. if (STBTE__IS_ACTIVE(id))
  1837. stbte__activate(0);
  1838. break;
  1839. }
  1840. if (*val >= v1-num_vis)
  1841. *val = v1-num_vis;
  1842. if (*val <= v0)
  1843. *val = v0;
  1844. }
  1845. static void stbte__compute_digits(stbte_tilemap *tm)
  1846. {
  1847. if (tm->max_x >= 1000 || tm->max_y >= 1000)
  1848. tm->digits = 4;
  1849. else if (tm->max_x >= 100 || tm->max_y >= 100)
  1850. tm->digits = 3;
  1851. else
  1852. tm->digits = 2;
  1853. }
  1854. static int stbte__is_single_selection(void)
  1855. {
  1856. return stbte__ui.has_selection
  1857. && stbte__ui.select_x0 == stbte__ui.select_x1
  1858. && stbte__ui.select_y0 == stbte__ui.select_y1;
  1859. }
  1860. typedef struct
  1861. {
  1862. int width, height;
  1863. int x,y;
  1864. int active;
  1865. float retracted;
  1866. } stbte__region_t;
  1867. static stbte__region_t stbte__region[4];
  1868. #define STBTE__TOOLBAR_ICON_SIZE (9+2*2)
  1869. #define STBTE__TOOLBAR_PASTE_SIZE (34+2*2)
  1870. // This routine computes where every panel goes onscreen: computes
  1871. // a minimum width for each side based on which panels are on that
  1872. // side, and accounts for width-dependent layout of certain panels.
  1873. static void stbte__compute_panel_locations(stbte_tilemap *tm)
  1874. {
  1875. int i, limit, w, k;
  1876. int window_width = stbte__ui.x1 - stbte__ui.x0;
  1877. int window_height = stbte__ui.y1 - stbte__ui.y0;
  1878. int min_width[STBTE__num_panel]={0,0,0,0,0,0,0};
  1879. int height[STBTE__num_panel]={0,0,0,0,0,0,0};
  1880. int panel_active[STBTE__num_panel]={1,0,1,1,1,1,1};
  1881. int vpos[4] = { 0,0,0,0 };
  1882. stbte__panel *p = stbte__ui.panel;
  1883. stbte__panel *pt = &p[STBTE__panel_toolbar];
  1884. #ifdef STBTE__NO_PROPS
  1885. int props = 0;
  1886. #else
  1887. int props = 1;
  1888. #endif
  1889. for (i=0; i < 4; ++i) {
  1890. stbte__region[i].active = 0;
  1891. stbte__region[i].width = 0;
  1892. stbte__region[i].height = 0;
  1893. }
  1894. // compute number of digits needs for info panel
  1895. stbte__compute_digits(tm);
  1896. // determine which panels are active
  1897. panel_active[STBTE__panel_categories] = tm->num_categories != 0;
  1898. panel_active[STBTE__panel_layers ] = tm->num_layers > 1;
  1899. #ifdef STBTE__COLORPICKER
  1900. panel_active[STBTE__panel_colorpick ] = 1;
  1901. #endif
  1902. panel_active[STBTE__panel_props ] = props && stbte__is_single_selection();
  1903. // compute minimum widths for each panel (assuming they're on sides not top)
  1904. min_width[STBTE__panel_info ] = 8 + 11 + 7*tm->digits+17+7; // estimate min width of "w:0000"
  1905. min_width[STBTE__panel_colorpick ] = 120;
  1906. min_width[STBTE__panel_tiles ] = 4 + tm->palette_spacing_x + 5; // 5 for scrollbar
  1907. min_width[STBTE__panel_categories] = 4 + 42 + 5; // 42 is enough to show ~7 chars; 5 for scrollbar
  1908. min_width[STBTE__panel_layers ] = 4 + 54 + 30*tm->has_layer_names; // 2 digits plus 3 buttons plus scrollbar
  1909. min_width[STBTE__panel_toolbar ] = 4 + STBTE__TOOLBAR_PASTE_SIZE; // wide enough for 'Paste' button
  1910. min_width[STBTE__panel_props ] = 80; // narrowest info panel
  1911. // compute minimum widths for left & right panels based on the above
  1912. stbte__region[0].width = stbte__ui.left_width;
  1913. stbte__region[1].width = stbte__ui.right_width;
  1914. for (i=0; i < STBTE__num_panel; ++i) {
  1915. if (panel_active[i]) {
  1916. int side = stbte__ui.panel[i].side;
  1917. if (min_width[i] > stbte__region[side].width)
  1918. stbte__region[side].width = min_width[i];
  1919. stbte__region[side].active = 1;
  1920. }
  1921. }
  1922. // now compute the heights of each panel
  1923. // if toolbar at top, compute its size & push the left and right start points down
  1924. if (stbte__region[STBTE__side_top].active) {
  1925. int height = STBTE__TOOLBAR_ICON_SIZE+2;
  1926. pt->x0 = stbte__ui.x0;
  1927. pt->y0 = stbte__ui.y0;
  1928. pt->width = window_width;
  1929. pt->height = height;
  1930. vpos[STBTE__side_left] = vpos[STBTE__side_right] = height;
  1931. } else {
  1932. int num_rows = STBTE__num_tool * ((stbte__region[pt->side].width-4)/STBTE__TOOLBAR_ICON_SIZE);
  1933. height[STBTE__panel_toolbar] = num_rows*13 + 3*15 + 4; // 3*15 for cut/copy/paste, which are stacked vertically
  1934. }
  1935. for (i=0; i < 4; ++i)
  1936. stbte__region[i].y = stbte__ui.y0 + vpos[i];
  1937. for (i=0; i < 2; ++i) {
  1938. int anim = (int) (stbte__region[i].width * stbte__region[i].retracted);
  1939. stbte__region[i].x = (i == STBTE__side_left) ? stbte__ui.x0 - anim : stbte__ui.x1 - stbte__region[i].width + anim;
  1940. }
  1941. // color picker
  1942. height[STBTE__panel_colorpick] = 300;
  1943. // info panel
  1944. w = stbte__region[p[STBTE__panel_info].side].width;
  1945. p[STBTE__panel_info].mode = (w >= 8 + (11+7*tm->digits+17)*2 + 4);
  1946. if (p[STBTE__panel_info].mode)
  1947. height[STBTE__panel_info] = 5 + 11*2 + 2 + tm->palette_spacing_y;
  1948. else
  1949. height[STBTE__panel_info] = 5 + 11*4 + 2 + tm->palette_spacing_y;
  1950. // layers
  1951. limit = 6 + stbte__ui.panel[STBTE__panel_layers].delta_height;
  1952. height[STBTE__panel_layers] = (tm->num_layers > limit ? limit : tm->num_layers)*15 + 7 + (tm->has_layer_names ? 0 : 11) + props*13;
  1953. // categories
  1954. limit = 6 + stbte__ui.panel[STBTE__panel_categories].delta_height;
  1955. height[STBTE__panel_categories] = (tm->num_categories+1 > limit ? limit : tm->num_categories+1)*11 + 14;
  1956. if (stbte__ui.panel[STBTE__panel_categories].side == stbte__ui.panel[STBTE__panel_categories].side)
  1957. height[STBTE__panel_categories] -= 4;
  1958. // palette
  1959. k = (stbte__region[p[STBTE__panel_tiles].side].width - 8) / tm->palette_spacing_x;
  1960. if (k == 0) k = 1;
  1961. height[STBTE__panel_tiles] = ((tm->num_tiles+k-1)/k) * tm->palette_spacing_y + 8;
  1962. // properties panel
  1963. height[STBTE__panel_props] = 9 + STBTE_MAX_PROPERTIES*14;
  1964. // now compute the locations of all the panels
  1965. for (i=0; i < STBTE__num_panel; ++i) {
  1966. if (panel_active[i]) {
  1967. int side = p[i].side;
  1968. if (side == STBTE__side_left || side == STBTE__side_right) {
  1969. p[i].width = stbte__region[side].width;
  1970. p[i].x0 = stbte__region[side].x;
  1971. p[i].y0 = stbte__ui.y0 + vpos[side];
  1972. p[i].height = height[i];
  1973. vpos[side] += height[i];
  1974. if (vpos[side] > window_height) {
  1975. vpos[side] = window_height;
  1976. p[i].height = stbte__ui.y1 - p[i].y0;
  1977. }
  1978. } else {
  1979. ; // it's at top, it's already been explicitly set up earlier
  1980. }
  1981. } else {
  1982. // inactive panel
  1983. p[i].height = 0;
  1984. p[i].width = 0;
  1985. p[i].x0 = stbte__ui.x1;
  1986. p[i].y0 = stbte__ui.y1;
  1987. }
  1988. }
  1989. }
  1990. // unique identifiers for imgui
  1991. enum
  1992. {
  1993. STBTE__map=1,
  1994. STBTE__region,
  1995. STBTE__panel, // panel background to hide map, and misc controls
  1996. STBTE__info, // info data
  1997. STBTE__toolbarA, STBTE__toolbarB, // toolbar buttons: param is tool number
  1998. STBTE__palette, // palette selectors: param is tile index
  1999. STBTE__categories, // category selectors: param is category index
  2000. STBTE__layer, //
  2001. STBTE__solo, STBTE__hide, STBTE__lock, // layer controls: param is layer
  2002. STBTE__scrollbar, // param is panel ID
  2003. STBTE__panel_mover, // p1 is panel ID, p2 is destination side
  2004. STBTE__panel_sizer, // param panel ID
  2005. STBTE__scrollbar_id,
  2006. STBTE__colorpick_id,
  2007. STBTE__prop_flag,
  2008. STBTE__prop_float,
  2009. STBTE__prop_int,
  2010. };
  2011. // id is: [ 24-bit data : 7-bit identifer ]
  2012. // map id is: [ 12-bit y : 12 bit x : 7-bit identifier ]
  2013. #define STBTE__ID(n,p) ((n) + ((p)<<7))
  2014. #define STBTE__ID2(n,p,q) STBTE__ID(n, ((p)<<12)+(q) )
  2015. #define STBTE__IDMAP(x,y) STBTE__ID2(STBTE__map, x,y)
  2016. static void stbte__activate_map(int x, int y)
  2017. {
  2018. stbte__ui.active_id = STBTE__IDMAP(x,y);
  2019. stbte__ui.active_event = stbte__ui.event;
  2020. stbte__ui.sx = x;
  2021. stbte__ui.sy = y;
  2022. }
  2023. static void stbte__alert(const char *msg)
  2024. {
  2025. stbte__ui.alert_msg = msg;
  2026. stbte__ui.alert_timer = 3;
  2027. }
  2028. #define STBTE__BG(tm,layer) ((layer) == 0 ? (tm)->background_tile : STBTE__NO_TILE)
  2029. static void stbte__brush_predict(stbte_tilemap *tm, short result[])
  2030. {
  2031. int layer_to_paint = tm->cur_layer;
  2032. stbte__tileinfo *ti;
  2033. int i;
  2034. if (tm->cur_tile < 0) return;
  2035. ti = &tm->tiles[tm->cur_tile];
  2036. // find lowest legit layer to paint it on, and put it there
  2037. for (i=0; i < tm->num_layers; ++i) {
  2038. // check if object is allowed on layer
  2039. if (!(ti->layermask & (1 << i)))
  2040. continue;
  2041. if (i != tm->solo_layer) {
  2042. // if there's a selected layer, can only paint on that
  2043. if (tm->cur_layer >= 0 && i != tm->cur_layer)
  2044. continue;
  2045. // if the layer is hidden, we can't see it
  2046. if (tm->layerinfo[i].hidden)
  2047. continue;
  2048. // if the layer is locked, we can't write to it
  2049. if (tm->layerinfo[i].locked == STBTE__locked)
  2050. continue;
  2051. // if the layer is non-empty and protected, can't write to it
  2052. if (tm->layerinfo[i].locked == STBTE__protected && result[i] != STBTE__BG(tm,i))
  2053. continue;
  2054. }
  2055. result[i] = ti->id;
  2056. return;
  2057. }
  2058. }
  2059. static void stbte__brush(stbte_tilemap *tm, int x, int y)
  2060. {
  2061. int layer_to_paint = tm->cur_layer;
  2062. stbte__tileinfo *ti;
  2063. // find lowest legit layer to paint it on, and put it there
  2064. int i;
  2065. if (tm->cur_tile < 0) return;
  2066. ti = &tm->tiles[tm->cur_tile];
  2067. for (i=0; i < tm->num_layers; ++i) {
  2068. // check if object is allowed on layer
  2069. if (!(ti->layermask & (1 << i)))
  2070. continue;
  2071. if (i != tm->solo_layer) {
  2072. // if there's a selected layer, can only paint on that
  2073. if (tm->cur_layer >= 0 && i != tm->cur_layer)
  2074. continue;
  2075. // if the layer is hidden, we can't see it
  2076. if (tm->layerinfo[i].hidden)
  2077. continue;
  2078. // if the layer is locked, we can't write to it
  2079. if (tm->layerinfo[i].locked == STBTE__locked)
  2080. continue;
  2081. // if the layer is non-empty and protected, can't write to it
  2082. if (tm->layerinfo[i].locked == STBTE__protected && tm->data[y][x][i] != STBTE__BG(tm,i))
  2083. continue;
  2084. }
  2085. stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
  2086. tm->data[y][x][i] = ti->id;
  2087. return;
  2088. }
  2089. //stbte__alert("Selected tile not valid on active layer(s)");
  2090. }
  2091. enum
  2092. {
  2093. STBTE__erase_none = -1,
  2094. STBTE__erase_brushonly = 0,
  2095. STBTE__erase_any = 1,
  2096. STBTE__erase_all = 2,
  2097. };
  2098. static int stbte__erase_predict(stbte_tilemap *tm, short result[], int allow_any)
  2099. {
  2100. stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL;
  2101. int i;
  2102. if (allow_any == STBTE__erase_none)
  2103. return allow_any;
  2104. // first check if only one layer is legit
  2105. i = tm->cur_layer;
  2106. if (tm->solo_layer >= 0)
  2107. i = tm->solo_layer;
  2108. // if only one layer is legit, directly process that one for clarity
  2109. if (i >= 0) {
  2110. short bg = (i == 0 ? tm->background_tile : -1);
  2111. if (tm->solo_layer < 0) {
  2112. // check that we're allowed to write to it
  2113. if (tm->layerinfo[i].hidden) return STBTE__erase_none;
  2114. if (tm->layerinfo[i].locked) return STBTE__erase_none;
  2115. }
  2116. if (result[i] == bg)
  2117. return STBTE__erase_none; // didn't erase anything
  2118. if (ti && result[i] == ti->id && (i != 0 || ti->id != tm->background_tile)) {
  2119. result[i] = bg;
  2120. return STBTE__erase_brushonly;
  2121. }
  2122. if (allow_any == STBTE__erase_any) {
  2123. result[i] = bg;
  2124. return STBTE__erase_any;
  2125. }
  2126. return STBTE__erase_none;
  2127. }
  2128. // if multiple layers are legit, first scan all for brush data
  2129. if (ti && allow_any != STBTE__erase_all) {
  2130. for (i=tm->num_layers-1; i >= 0; --i) {
  2131. if (result[i] != ti->id)
  2132. continue;
  2133. if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
  2134. continue;
  2135. if (i == 0 && result[i] == tm->background_tile)
  2136. return STBTE__erase_none;
  2137. result[i] = STBTE__BG(tm,i);
  2138. return STBTE__erase_brushonly;
  2139. }
  2140. }
  2141. if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all)
  2142. return STBTE__erase_none;
  2143. // apply layer filters, erase from top
  2144. for (i=tm->num_layers-1; i >= 0; --i) {
  2145. if (result[i] < 0)
  2146. continue;
  2147. if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
  2148. continue;
  2149. if (i == 0 && result[i] == tm->background_tile)
  2150. return STBTE__erase_none;
  2151. result[i] = STBTE__BG(tm,i);
  2152. if (allow_any != STBTE__erase_all)
  2153. return STBTE__erase_any;
  2154. }
  2155. if (allow_any == STBTE__erase_all)
  2156. return allow_any;
  2157. return STBTE__erase_none;
  2158. }
  2159. static int stbte__erase(stbte_tilemap *tm, int x, int y, int allow_any)
  2160. {
  2161. stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL;
  2162. int i;
  2163. if (allow_any == STBTE__erase_none)
  2164. return allow_any;
  2165. // first check if only one layer is legit
  2166. i = tm->cur_layer;
  2167. if (tm->solo_layer >= 0)
  2168. i = tm->solo_layer;
  2169. // if only one layer is legit, directly process that one for clarity
  2170. if (i >= 0) {
  2171. short bg = (i == 0 ? tm->background_tile : -1);
  2172. if (tm->solo_layer < 0) {
  2173. // check that we're allowed to write to it
  2174. if (tm->layerinfo[i].hidden) return STBTE__erase_none;
  2175. if (tm->layerinfo[i].locked) return STBTE__erase_none;
  2176. }
  2177. if (tm->data[y][x][i] == bg)
  2178. return -1; // didn't erase anything
  2179. if (ti && tm->data[y][x][i] == ti->id && (i != 0 || ti->id != tm->background_tile)) {
  2180. stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
  2181. tm->data[y][x][i] = bg;
  2182. return STBTE__erase_brushonly;
  2183. }
  2184. if (allow_any == STBTE__erase_any) {
  2185. stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
  2186. tm->data[y][x][i] = bg;
  2187. return STBTE__erase_any;
  2188. }
  2189. return STBTE__erase_none;
  2190. }
  2191. // if multiple layers are legit, first scan all for brush data
  2192. if (ti && allow_any != STBTE__erase_all) {
  2193. for (i=tm->num_layers-1; i >= 0; --i) {
  2194. if (tm->data[y][x][i] != ti->id)
  2195. continue;
  2196. if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
  2197. continue;
  2198. if (i == 0 && tm->data[y][x][i] == tm->background_tile)
  2199. return STBTE__erase_none;
  2200. stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
  2201. tm->data[y][x][i] = STBTE__BG(tm,i);
  2202. return STBTE__erase_brushonly;
  2203. }
  2204. }
  2205. if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all)
  2206. return STBTE__erase_none;
  2207. // apply layer filters, erase from top
  2208. for (i=tm->num_layers-1; i >= 0; --i) {
  2209. if (tm->data[y][x][i] < 0)
  2210. continue;
  2211. if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
  2212. continue;
  2213. if (i == 0 && tm->data[y][x][i] == tm->background_tile)
  2214. return STBTE__erase_none;
  2215. stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
  2216. tm->data[y][x][i] = STBTE__BG(tm,i);
  2217. if (allow_any != STBTE__erase_all)
  2218. return STBTE__erase_any;
  2219. }
  2220. if (allow_any == STBTE__erase_all)
  2221. return allow_any;
  2222. return STBTE__erase_none;
  2223. }
  2224. static int stbte__find_tile(stbte_tilemap *tm, int tile_id)
  2225. {
  2226. int i;
  2227. for (i=0; i < tm->num_tiles; ++i)
  2228. if (tm->tiles[i].id == tile_id)
  2229. return i;
  2230. stbte__alert("Eyedropped tile that isn't in tileset");
  2231. return -1;
  2232. }
  2233. static void stbte__eyedrop(stbte_tilemap *tm, int x, int y)
  2234. {
  2235. int i,j;
  2236. // flush eyedropper state
  2237. if (stbte__ui.eyedrop_x != x || stbte__ui.eyedrop_y != y) {
  2238. stbte__ui.eyedrop_x = x;
  2239. stbte__ui.eyedrop_y = y;
  2240. stbte__ui.eyedrop_last_layer = tm->num_layers;
  2241. }
  2242. // if only one layer is active, query that
  2243. i = tm->cur_layer;
  2244. if (tm->solo_layer >= 0)
  2245. i = tm->solo_layer;
  2246. if (i >= 0) {
  2247. if (tm->data[y][x][i] == STBTE__NO_TILE)
  2248. return;
  2249. tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]);
  2250. return;
  2251. }
  2252. // if multiple layers, continue from previous
  2253. i = stbte__ui.eyedrop_last_layer;
  2254. for (j=0; j < tm->num_layers; ++j) {
  2255. if (--i < 0)
  2256. i = tm->num_layers-1;
  2257. if (tm->layerinfo[i].hidden)
  2258. continue;
  2259. if (tm->data[y][x][i] == STBTE__NO_TILE)
  2260. continue;
  2261. stbte__ui.eyedrop_last_layer = i;
  2262. tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]);
  2263. return;
  2264. }
  2265. }
  2266. static int stbte__should_copy_properties(stbte_tilemap *tm)
  2267. {
  2268. int i;
  2269. if (tm->propmode == STBTE__propmode_always)
  2270. return 1;
  2271. if (tm->propmode == STBTE__propmode_never)
  2272. return 0;
  2273. if (tm->solo_layer >= 0 || tm->cur_layer >= 0)
  2274. return 0;
  2275. for (i=0; i < tm->num_layers; ++i)
  2276. if (tm->layerinfo[i].hidden || tm->layerinfo[i].locked)
  2277. return 0;
  2278. return 1;
  2279. }
  2280. // compute the result of pasting into a tile non-destructively so we can preview it
  2281. static void stbte__paste_stack(stbte_tilemap *tm, short result[], short dest[], short src[], int dragging)
  2282. {
  2283. int i;
  2284. // special case single-layer
  2285. i = tm->cur_layer;
  2286. if (tm->solo_layer >= 0)
  2287. i = tm->solo_layer;
  2288. if (i >= 0) {
  2289. if (tm->solo_layer < 0) {
  2290. // check that we're allowed to write to it
  2291. if (tm->layerinfo[i].hidden) return;
  2292. if (tm->layerinfo[i].locked == STBTE__locked) return;
  2293. // if protected, dest has to be empty
  2294. if (tm->layerinfo[i].locked == STBTE__protected && dest[i] != STBTE__BG(tm,i)) return;
  2295. // if dragging w/o copy, we will try to erase stuff, which protection disallows
  2296. if (dragging && tm->layerinfo[i].locked == STBTE__protected)
  2297. return;
  2298. }
  2299. result[i] = dest[i];
  2300. if (src[i] != STBTE__BG(tm,i))
  2301. result[i] = src[i];
  2302. return;
  2303. }
  2304. for (i=0; i < tm->num_layers; ++i) {
  2305. result[i] = dest[i];
  2306. if (src[i] != STBTE__NO_TILE)
  2307. if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked != STBTE__locked)
  2308. if (tm->layerinfo[i].locked == STBTE__unlocked || (!dragging && dest[i] == STBTE__BG(tm,i)))
  2309. result[i] = src[i];
  2310. }
  2311. }
  2312. // compute the result of dragging away from a tile
  2313. static void stbte__clear_stack(stbte_tilemap *tm, short result[])
  2314. {
  2315. int i;
  2316. // special case single-layer
  2317. i = tm->cur_layer;
  2318. if (tm->solo_layer >= 0)
  2319. i = tm->solo_layer;
  2320. if (i >= 0)
  2321. result[i] = STBTE__BG(tm,i);
  2322. else
  2323. for (i=0; i < tm->num_layers; ++i)
  2324. if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked == STBTE__unlocked)
  2325. result[i] = STBTE__BG(tm,i);
  2326. }
  2327. // check if some map square is active
  2328. #define STBTE__IS_MAP_ACTIVE() ((stbte__ui.active_id & 127) == STBTE__map)
  2329. #define STBTE__IS_MAP_HOT() ((stbte__ui.hot_id & 127) == STBTE__map)
  2330. static void stbte__fillrect(stbte_tilemap *tm, int x0, int y0, int x1, int y1, int fill)
  2331. {
  2332. int i,j;
  2333. int x=x0,y=y0;
  2334. stbte__begin_undo(tm);
  2335. if (x0 > x1) i=x0,x0=x1,x1=i;
  2336. if (y0 > y1) j=y0,y0=y1,y1=j;
  2337. for (j=y0; j <= y1; ++j)
  2338. for (i=x0; i <= x1; ++i)
  2339. if (fill)
  2340. stbte__brush(tm, i,j);
  2341. else
  2342. stbte__erase(tm, i,j,STBTE__erase_any);
  2343. stbte__end_undo(tm);
  2344. // suppress warning from brush
  2345. stbte__ui.alert_msg = 0;
  2346. }
  2347. static void stbte__select_rect(stbte_tilemap *tm, int x0, int y0, int x1, int y1)
  2348. {
  2349. stbte__ui.has_selection = 1;
  2350. stbte__ui.select_x0 = (x0 < x1 ? x0 : x1);
  2351. stbte__ui.select_x1 = (x0 < x1 ? x1 : x0);
  2352. stbte__ui.select_y0 = (y0 < y1 ? y0 : y1);
  2353. stbte__ui.select_y1 = (y0 < y1 ? y1 : y0);
  2354. }
  2355. static void stbte__copy_properties(float *dest, float *src)
  2356. {
  2357. int i;
  2358. for (i=0; i < STBTE_MAX_PROPERTIES; ++i)
  2359. dest[i] = src[i];
  2360. }
  2361. static void stbte__copy_cut(stbte_tilemap *tm, int cut)
  2362. {
  2363. int i,j,n,w,h,p=0;
  2364. int copy_props = stbte__should_copy_properties(tm);
  2365. if (!stbte__ui.has_selection)
  2366. return;
  2367. w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1;
  2368. h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1;
  2369. if (STBTE_MAX_COPY / w < h) {
  2370. stbte__alert("Selection too large for copy buffer, increase STBTE_MAX_COPY");
  2371. return;
  2372. }
  2373. for (i=0; i < w*h; ++i)
  2374. for (n=0; n < tm->num_layers; ++n)
  2375. stbte__ui.copybuffer[i][n] = STBTE__NO_TILE;
  2376. if (cut)
  2377. stbte__begin_undo(tm);
  2378. for (j=stbte__ui.select_y0; j <= stbte__ui.select_y1; ++j) {
  2379. for (i=stbte__ui.select_x0; i <= stbte__ui.select_x1; ++i) {
  2380. for (n=0; n < tm->num_layers; ++n) {
  2381. if (tm->solo_layer >= 0) {
  2382. if (tm->solo_layer != n)
  2383. continue;
  2384. } else {
  2385. if (tm->cur_layer >= 0)
  2386. if (tm->cur_layer != n)
  2387. continue;
  2388. if (tm->layerinfo[n].hidden)
  2389. continue;
  2390. if (cut && tm->layerinfo[n].locked)
  2391. continue;
  2392. }
  2393. stbte__ui.copybuffer[p][n] = tm->data[j][i][n];
  2394. if (cut) {
  2395. stbte__undo_record(tm,i,j,n, tm->data[j][i][n]);
  2396. tm->data[j][i][n] = (n==0 ? tm->background_tile : -1);
  2397. }
  2398. }
  2399. if (copy_props) {
  2400. stbte__copy_properties(stbte__ui.copyprops[p], tm->props[j][i]);
  2401. #ifdef STBTE_ALLOW_LINK
  2402. stbte__ui.copylinks[p] = tm->link[j][i];
  2403. if (cut)
  2404. stbte__set_link(tm, i,j,-1,-1, STBTE__undo_record);
  2405. #endif
  2406. }
  2407. ++p;
  2408. }
  2409. }
  2410. if (cut)
  2411. stbte__end_undo(tm);
  2412. stbte__ui.copy_width = w;
  2413. stbte__ui.copy_height = h;
  2414. stbte__ui.has_copy = 1;
  2415. //stbte__ui.has_selection = 0;
  2416. stbte__ui.copy_has_props = copy_props;
  2417. stbte__ui.copy_src = tm; // used to give better semantics when copying links
  2418. stbte__ui.copy_src_x = stbte__ui.select_x0;
  2419. stbte__ui.copy_src_y = stbte__ui.select_y0;
  2420. }
  2421. static int stbte__in_rect(int x, int y, int x0, int y0, int w, int h)
  2422. {
  2423. return x >= x0 && x < x0+w && y >= y0 && y < y0+h;
  2424. }
  2425. static int stbte__in_src_rect(int x, int y)
  2426. {
  2427. return stbte__in_rect(x,y, stbte__ui.copy_src_x, stbte__ui.copy_src_y, stbte__ui.copy_width, stbte__ui.copy_height);
  2428. }
  2429. static int stbte__in_dest_rect(int x, int y, int destx, int desty)
  2430. {
  2431. return stbte__in_rect(x,y, destx, desty, stbte__ui.copy_width, stbte__ui.copy_height);
  2432. }
  2433. static void stbte__paste(stbte_tilemap *tm, int mapx, int mapy)
  2434. {
  2435. int w = stbte__ui.copy_width;
  2436. int h = stbte__ui.copy_height;
  2437. int i,j,k,p;
  2438. int x = mapx - (w>>1);
  2439. int y = mapy - (h>>1);
  2440. int copy_props = stbte__should_copy_properties(tm) && stbte__ui.copy_has_props;
  2441. if (stbte__ui.has_copy == 0)
  2442. return;
  2443. stbte__begin_undo(tm);
  2444. p = 0;
  2445. for (j=0; j < h; ++j) {
  2446. for (i=0; i < w; ++i) {
  2447. if (y+j >= 0 && y+j < tm->max_y && x+i >= 0 && x+i < tm->max_x) {
  2448. // compute the new stack
  2449. short tilestack[STBTE_MAX_LAYERS];
  2450. for (k=0; k < tm->num_layers; ++k)
  2451. tilestack[k] = tm->data[y+j][x+i][k];
  2452. stbte__paste_stack(tm, tilestack, tilestack, stbte__ui.copybuffer[p], 0);
  2453. // update anything that changed
  2454. for (k=0; k < tm->num_layers; ++k) {
  2455. if (tilestack[k] != tm->data[y+j][x+i][k]) {
  2456. stbte__undo_record(tm, x+i,y+j,k, tm->data[y+j][x+i][k]);
  2457. tm->data[y+j][x+i][k] = tilestack[k];
  2458. }
  2459. }
  2460. }
  2461. if (copy_props) {
  2462. #ifdef STBTE_ALLOW_LINK
  2463. // need to decide how to paste a link, so there's a few cases
  2464. int destx = -1, desty = -1;
  2465. stbte__link *link = &stbte__ui.copylinks[p];
  2466. // check if link is within-rect
  2467. if (stbte__in_src_rect(link->x, link->y)) {
  2468. // new link should point to copy (but only if copy is within map)
  2469. destx = x + (link->x - stbte__ui.copy_src_x);
  2470. desty = y + (link->y - stbte__ui.copy_src_y);
  2471. } else if (tm == stbte__ui.copy_src) {
  2472. // if same map, then preserve link unless target is overwritten
  2473. if (!stbte__in_dest_rect(link->x,link->y,x,y)) {
  2474. destx = link->x;
  2475. desty = link->y;
  2476. }
  2477. }
  2478. // this is necessary for offset-copy, but also in case max_x/max_y has changed
  2479. if (destx < 0 || destx >= tm->max_x || desty < 0 || desty >= tm->max_y)
  2480. destx = -1, desty = -1;
  2481. stbte__set_link(tm, x+i, y+j, destx, desty, STBTE__undo_record);
  2482. #endif
  2483. for (k=0; k < STBTE_MAX_PROPERTIES; ++k) {
  2484. if (tm->props[y+j][x+i][k] != stbte__ui.copyprops[p][k])
  2485. stbte__undo_record_prop_float(tm, x+i, y+j, k, tm->props[y+j][x+i][k]);
  2486. }
  2487. stbte__copy_properties(tm->props[y+j][x+i], stbte__ui.copyprops[p]);
  2488. }
  2489. ++p;
  2490. }
  2491. }
  2492. stbte__end_undo(tm);
  2493. }
  2494. static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy, int copy_props)
  2495. {
  2496. int w = stbte__ui.drag_w, h = stbte__ui.drag_h;
  2497. int ox,oy,i,deleted=0,written=0;
  2498. short temp[STBTE_MAX_LAYERS];
  2499. short *data = NULL;
  2500. if (!stbte__ui.shift) {
  2501. ox = mapx - stbte__ui.drag_x;
  2502. oy = mapy - stbte__ui.drag_y;
  2503. if (ox >= 0 && ox < w && oy >= 0 && oy < h) {
  2504. deleted=1;
  2505. for (i=0; i < tm->num_layers; ++i)
  2506. temp[i] = tm->data[mapy][mapx][i];
  2507. data = temp;
  2508. stbte__clear_stack(tm, data);
  2509. }
  2510. }
  2511. ox = mapx - stbte__ui.drag_dest_x;
  2512. oy = mapy - stbte__ui.drag_dest_y;
  2513. // if this map square is in the target drag region
  2514. if (ox >= 0 && ox < w && oy >= 0 && oy < h) {
  2515. // and the src map square is on the map
  2516. if (stbte__in_rect(stbte__ui.drag_x+ox, stbte__ui.drag_y+oy, 0, 0, tm->max_x, tm->max_y)) {
  2517. written = 1;
  2518. if (data == NULL) {
  2519. for (i=0; i < tm->num_layers; ++i)
  2520. temp[i] = tm->data[mapy][mapx][i];
  2521. data = temp;
  2522. }
  2523. stbte__paste_stack(tm, data, data, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift);
  2524. if (copy_props) {
  2525. for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
  2526. if (tm->props[mapy][mapx][i] != tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]) {
  2527. stbte__undo_record_prop_float(tm, mapx, mapy, i, tm->props[mapy][mapx][i]);
  2528. tm->props[mapy][mapx][i] = tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i];
  2529. }
  2530. }
  2531. }
  2532. }
  2533. }
  2534. if (data) {
  2535. for (i=0; i < tm->num_layers; ++i) {
  2536. if (tm->data[mapy][mapx][i] != data[i]) {
  2537. stbte__undo_record(tm, mapx, mapy, i, tm->data[mapy][mapx][i]);
  2538. tm->data[mapy][mapx][i] = data[i];
  2539. }
  2540. }
  2541. }
  2542. #ifdef STBTE_ALLOW_LINK
  2543. if (copy_props) {
  2544. int overwritten=0, moved=0, copied=0;
  2545. // since this function is called on EVERY tile, we can fix up even tiles not
  2546. // involved in the move
  2547. stbte__link *k;
  2548. // first, determine what src link ends up here
  2549. k = &tm->link[mapy][mapx]; // by default, it's the one currently here
  2550. if (deleted) // if dragged away, it's erased
  2551. k = NULL;
  2552. if (written) // if dragged into, it gets that link
  2553. k = &tm->link[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox];
  2554. // now check whether the *target* gets moved or overwritten
  2555. if (k && k->x >= 0) {
  2556. overwritten = stbte__in_rect(k->x, k->y, stbte__ui.drag_dest_x, stbte__ui.drag_dest_y, w, h);
  2557. if (!stbte__ui.shift)
  2558. moved = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h);
  2559. else
  2560. copied = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h);
  2561. }
  2562. if (deleted || written || overwritten || moved || copied) {
  2563. // choose the final link value based on the above
  2564. if (k == NULL || k->x < 0)
  2565. stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record);
  2566. else if (moved || (copied && written)) {
  2567. // if we move the target, we update to point to the new target;
  2568. // or, if we copy the target and the source is part ofthe copy, then update to new target
  2569. int x = k->x + (stbte__ui.drag_dest_x - stbte__ui.drag_x);
  2570. int y = k->y + (stbte__ui.drag_dest_y - stbte__ui.drag_y);
  2571. if (!(x >= 0 && y >= 0 && x < tm->max_x && y < tm->max_y))
  2572. x = -1, y = -1;
  2573. stbte__set_link(tm, mapx, mapy, x, y, STBTE__undo_record);
  2574. } else if (overwritten) {
  2575. stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record);
  2576. } else
  2577. stbte__set_link(tm, mapx, mapy, k->x, k->y, STBTE__undo_record);
  2578. }
  2579. }
  2580. #endif
  2581. }
  2582. static void stbte__drag_place(stbte_tilemap *tm, int mapx, int mapy)
  2583. {
  2584. int i,j;
  2585. int copy_props = stbte__should_copy_properties(tm);
  2586. int move_x = (stbte__ui.drag_dest_x - stbte__ui.drag_x);
  2587. int move_y = (stbte__ui.drag_dest_y - stbte__ui.drag_y);
  2588. if (move_x == 0 && move_y == 0)
  2589. return;
  2590. stbte__begin_undo(tm);
  2591. // we now need a 2D memmove-style mover that doesn't
  2592. // overwrite any data as it goes. this requires being
  2593. // direction sensitive in the same way as memmove
  2594. if (move_y > 0 || (move_y == 0 && move_x > 0)) {
  2595. for (j=tm->max_y-1; j >= 0; --j)
  2596. for (i=tm->max_x-1; i >= 0; --i)
  2597. stbte__drag_update(tm,i,j,copy_props);
  2598. } else {
  2599. for (j=0; j < tm->max_y; ++j)
  2600. for (i=0; i < tm->max_x; ++i)
  2601. stbte__drag_update(tm,i,j,copy_props);
  2602. }
  2603. stbte__end_undo(tm);
  2604. stbte__ui.has_selection = 1;
  2605. stbte__ui.select_x0 = stbte__ui.drag_dest_x;
  2606. stbte__ui.select_y0 = stbte__ui.drag_dest_y;
  2607. stbte__ui.select_x1 = stbte__ui.select_x0 + stbte__ui.drag_w - 1;
  2608. stbte__ui.select_y1 = stbte__ui.select_y0 + stbte__ui.drag_h - 1;
  2609. }
  2610. static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy, int layer)
  2611. {
  2612. int i;
  2613. int id = STBTE__IDMAP(mapx,mapy);
  2614. int x0=sx, y0=sy;
  2615. int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
  2616. int over = stbte__hittest(x0,y0,x1,y1, id);
  2617. short *data = tm->data[mapy][mapx];
  2618. short temp[STBTE_MAX_LAYERS];
  2619. if (STBTE__IS_MAP_HOT()) {
  2620. if (stbte__ui.pasting) {
  2621. int ox = mapx - stbte__ui.paste_x;
  2622. int oy = mapy - stbte__ui.paste_y;
  2623. if (ox >= 0 && ox < stbte__ui.copy_width && oy >= 0 && oy < stbte__ui.copy_height) {
  2624. stbte__paste_stack(tm, temp, tm->data[mapy][mapx], stbte__ui.copybuffer[oy*stbte__ui.copy_width+ox], 0);
  2625. data = temp;
  2626. }
  2627. } else if (stbte__ui.dragging) {
  2628. int ox,oy;
  2629. for (i=0; i < tm->num_layers; ++i)
  2630. temp[i] = tm->data[mapy][mapx][i];
  2631. data = temp;
  2632. // if it's in the source area, remove things unless shift-dragging
  2633. ox = mapx - stbte__ui.drag_x;
  2634. oy = mapy - stbte__ui.drag_y;
  2635. if (!stbte__ui.shift && ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) {
  2636. stbte__clear_stack(tm, temp);
  2637. }
  2638. ox = mapx - stbte__ui.drag_dest_x;
  2639. oy = mapy - stbte__ui.drag_dest_y;
  2640. if (ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) {
  2641. stbte__paste_stack(tm, temp, temp, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift);
  2642. }
  2643. } else if (STBTE__IS_MAP_ACTIVE()) {
  2644. if (stbte__ui.tool == STBTE__tool_rect) {
  2645. if ((stbte__ui.ms_time & 511) < 380) {
  2646. int ex = ((stbte__ui.hot_id >> 19) & 4095);
  2647. int ey = ((stbte__ui.hot_id >> 7) & 4095);
  2648. int sx = stbte__ui.sx;
  2649. int sy = stbte__ui.sy;
  2650. if ( ((mapx >= sx && mapx < ex+1) || (mapx >= ex && mapx < sx+1))
  2651. && ((mapy >= sy && mapy < ey+1) || (mapy >= ey && mapy < sy+1))) {
  2652. int i;
  2653. for (i=0; i < tm->num_layers; ++i)
  2654. temp[i] = tm->data[mapy][mapx][i];
  2655. data = temp;
  2656. if (stbte__ui.active_event == STBTE__leftdown)
  2657. stbte__brush_predict(tm, temp);
  2658. else
  2659. stbte__erase_predict(tm, temp, STBTE__erase_any);
  2660. }
  2661. }
  2662. }
  2663. }
  2664. }
  2665. if (STBTE__IS_HOT(id) && STBTE__INACTIVE() && !stbte__ui.pasting) {
  2666. if (stbte__ui.tool == STBTE__tool_brush) {
  2667. if ((stbte__ui.ms_time & 511) < 300) {
  2668. data = temp;
  2669. for (i=0; i < tm->num_layers; ++i)
  2670. temp[i] = tm->data[mapy][mapx][i];
  2671. stbte__brush_predict(tm, temp);
  2672. }
  2673. }
  2674. }
  2675. {
  2676. i = layer;
  2677. if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0))
  2678. if (data[i] >= 0)
  2679. STBTE_DRAW_TILE(x0,y0, (unsigned short) data[i], 0, tm->props[mapy][mapx]);
  2680. }
  2681. }
  2682. static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy)
  2683. {
  2684. int tool = stbte__ui.tool;
  2685. int x0=sx, y0=sy;
  2686. int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
  2687. int id = STBTE__IDMAP(mapx,mapy);
  2688. int over = stbte__hittest(x0,y0,x1,y1, id);
  2689. switch (stbte__ui.event) {
  2690. case STBTE__paint: {
  2691. if (stbte__ui.pasting || stbte__ui.dragging || stbte__ui.scrolling)
  2692. break;
  2693. if (stbte__ui.scrollkey && !STBTE__IS_MAP_ACTIVE())
  2694. break;
  2695. if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE() && (tool == STBTE__tool_rect || tool == STBTE__tool_select)) {
  2696. int rx0,ry0,rx1,ry1,t;
  2697. // compute the center of each rect
  2698. rx0 = x0 + tm->spacing_x/2;
  2699. ry0 = y0 + tm->spacing_y/2;
  2700. rx1 = rx0 + (stbte__ui.sx - mapx) * tm->spacing_x;
  2701. ry1 = ry0 + (stbte__ui.sy - mapy) * tm->spacing_y;
  2702. if (rx0 > rx1) t=rx0,rx0=rx1,rx1=t;
  2703. if (ry0 > ry1) t=ry0,ry0=ry1,ry1=t;
  2704. rx0 -= tm->spacing_x/2;
  2705. ry0 -= tm->spacing_y/2;
  2706. rx1 += tm->spacing_x/2;
  2707. ry1 += tm->spacing_y/2;
  2708. stbte__draw_frame(rx0-1,ry0-1,rx1+1,ry1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT);
  2709. break;
  2710. }
  2711. if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
  2712. stbte__draw_frame(x0-1,y0-1,x1+1,y1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT);
  2713. }
  2714. #ifdef STBTE_ALLOW_LINK
  2715. if (stbte__ui.show_links && tm->link[mapy][mapx].x >= 0) {
  2716. int tx = tm->link[mapy][mapx].x;
  2717. int ty = tm->link[mapy][mapx].y;
  2718. int lx0,ly0,lx1,ly1;
  2719. if (STBTE_ALLOW_LINK(tm->data[mapy][mapx], tm->props[mapy][mapx],
  2720. tm->data[ty ][tx ], tm->props[ty ][tx ]))
  2721. {
  2722. lx0 = x0 + (tm->spacing_x >> 1) - 1;
  2723. ly0 = y0 + (tm->spacing_y >> 1) - 1;
  2724. lx1 = lx0 + (tx - mapx) * tm->spacing_x + 2;
  2725. ly1 = ly0 + (ty - mapy) * tm->spacing_y + 2;
  2726. stbte__draw_link(lx0,ly0,lx1,ly1,
  2727. STBTE_LINK_COLOR(tm->data[mapy][mapx], tm->props[mapy][mapx],
  2728. tm->data[ty ][tx ], tm->props[ty ][tx]));
  2729. }
  2730. }
  2731. #endif
  2732. break;
  2733. }
  2734. }
  2735. if (stbte__ui.pasting) {
  2736. switch (stbte__ui.event) {
  2737. case STBTE__leftdown:
  2738. if (STBTE__IS_HOT(id)) {
  2739. stbte__ui.pasting = 0;
  2740. stbte__paste(tm, mapx, mapy);
  2741. stbte__activate(0);
  2742. }
  2743. break;
  2744. case STBTE__leftup:
  2745. // just clear it no matter what, since they might click away to clear it
  2746. stbte__activate(0);
  2747. break;
  2748. case STBTE__rightdown:
  2749. if (STBTE__IS_HOT(id)) {
  2750. stbte__activate(0);
  2751. stbte__ui.pasting = 0;
  2752. }
  2753. break;
  2754. }
  2755. return;
  2756. }
  2757. if (stbte__ui.scrolling) {
  2758. if (stbte__ui.event == STBTE__leftup) {
  2759. stbte__activate(0);
  2760. stbte__ui.scrolling = 0;
  2761. }
  2762. if (stbte__ui.event == STBTE__mousemove) {
  2763. tm->scroll_x += (stbte__ui.start_x - stbte__ui.mx);
  2764. tm->scroll_y += (stbte__ui.start_y - stbte__ui.my);
  2765. stbte__ui.start_x = stbte__ui.mx;
  2766. stbte__ui.start_y = stbte__ui.my;
  2767. }
  2768. return;
  2769. }
  2770. // regardless of tool, leftdown is a scrolldrag
  2771. if (STBTE__IS_HOT(id) && stbte__ui.scrollkey && stbte__ui.event == STBTE__leftdown) {
  2772. stbte__ui.scrolling = 1;
  2773. stbte__ui.start_x = stbte__ui.mx;
  2774. stbte__ui.start_y = stbte__ui.my;
  2775. return;
  2776. }
  2777. switch (tool) {
  2778. case STBTE__tool_brush:
  2779. switch (stbte__ui.event) {
  2780. case STBTE__mousemove:
  2781. if (STBTE__IS_MAP_ACTIVE() && over) {
  2782. // don't brush/erase same tile multiple times unless they move away and back @TODO should just be only once, but that needs another data structure
  2783. if (!STBTE__IS_ACTIVE(id)) {
  2784. if (stbte__ui.active_event == STBTE__leftdown)
  2785. stbte__brush(tm, mapx, mapy);
  2786. else
  2787. stbte__erase(tm, mapx, mapy, stbte__ui.brush_state);
  2788. stbte__ui.active_id = id; // switch to this map square so we don't rebrush IT multiple times
  2789. }
  2790. }
  2791. break;
  2792. case STBTE__leftdown:
  2793. if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
  2794. stbte__activate(id);
  2795. stbte__begin_undo(tm);
  2796. stbte__brush(tm, mapx, mapy);
  2797. }
  2798. break;
  2799. case STBTE__rightdown:
  2800. if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
  2801. stbte__activate(id);
  2802. stbte__begin_undo(tm);
  2803. if (stbte__erase(tm, mapx, mapy, STBTE__erase_any) == STBTE__erase_brushonly)
  2804. stbte__ui.brush_state = STBTE__erase_brushonly;
  2805. else
  2806. stbte__ui.brush_state = STBTE__erase_any;
  2807. }
  2808. break;
  2809. case STBTE__leftup:
  2810. case STBTE__rightup:
  2811. if (STBTE__IS_MAP_ACTIVE()) {
  2812. stbte__end_undo(tm);
  2813. stbte__activate(0);
  2814. }
  2815. break;
  2816. }
  2817. break;
  2818. #ifdef STBTE_ALLOW_LINK
  2819. case STBTE__tool_link:
  2820. switch (stbte__ui.event) {
  2821. case STBTE__leftdown:
  2822. if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
  2823. stbte__activate(id);
  2824. stbte__ui.linking = 1;
  2825. stbte__ui.sx = mapx;
  2826. stbte__ui.sy = mapy;
  2827. // @TODO: undo
  2828. }
  2829. break;
  2830. case STBTE__leftup:
  2831. if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE()) {
  2832. if ((mapx != stbte__ui.sx || mapy != stbte__ui.sy) &&
  2833. STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx],
  2834. tm->data[mapy][mapx], tm->props[mapy][mapx]))
  2835. stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, STBTE__undo_block);
  2836. else
  2837. stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, -1,-1, STBTE__undo_block);
  2838. stbte__ui.linking = 0;
  2839. stbte__activate(0);
  2840. }
  2841. break;
  2842. case STBTE__rightdown:
  2843. if (STBTE__IS_ACTIVE(id)) {
  2844. stbte__activate(0);
  2845. stbte__ui.linking = 0;
  2846. }
  2847. break;
  2848. }
  2849. break;
  2850. #endif
  2851. case STBTE__tool_erase:
  2852. switch (stbte__ui.event) {
  2853. case STBTE__mousemove:
  2854. if (STBTE__IS_MAP_ACTIVE() && over)
  2855. stbte__erase(tm, mapx, mapy, STBTE__erase_all);
  2856. break;
  2857. case STBTE__leftdown:
  2858. if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
  2859. stbte__activate(id);
  2860. stbte__begin_undo(tm);
  2861. stbte__erase(tm, mapx, mapy, STBTE__erase_all);
  2862. }
  2863. break;
  2864. case STBTE__leftup:
  2865. if (STBTE__IS_MAP_ACTIVE()) {
  2866. stbte__end_undo(tm);
  2867. stbte__activate(0);
  2868. }
  2869. break;
  2870. }
  2871. break;
  2872. case STBTE__tool_select:
  2873. if (STBTE__IS_HOT(id)) {
  2874. switch (stbte__ui.event) {
  2875. case STBTE__leftdown:
  2876. if (STBTE__INACTIVE()) {
  2877. // if we're clicking in an existing selection...
  2878. if (stbte__ui.has_selection) {
  2879. if ( mapx >= stbte__ui.select_x0 && mapx <= stbte__ui.select_x1
  2880. && mapy >= stbte__ui.select_y0 && mapy <= stbte__ui.select_y1)
  2881. {
  2882. stbte__ui.dragging = 1;
  2883. stbte__ui.drag_x = stbte__ui.select_x0;
  2884. stbte__ui.drag_y = stbte__ui.select_y0;
  2885. stbte__ui.drag_w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1;
  2886. stbte__ui.drag_h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1;
  2887. stbte__ui.drag_offx = mapx - stbte__ui.select_x0;
  2888. stbte__ui.drag_offy = mapy - stbte__ui.select_y0;
  2889. }
  2890. }
  2891. stbte__ui.has_selection = 0; // no selection until it completes
  2892. stbte__activate_map(mapx,mapy);
  2893. }
  2894. break;
  2895. case STBTE__leftup:
  2896. if (STBTE__IS_MAP_ACTIVE()) {
  2897. if (stbte__ui.dragging) {
  2898. stbte__drag_place(tm, mapx,mapy);
  2899. stbte__ui.dragging = 0;
  2900. stbte__activate(0);
  2901. } else {
  2902. stbte__select_rect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy);
  2903. stbte__activate(0);
  2904. }
  2905. }
  2906. break;
  2907. case STBTE__rightdown:
  2908. stbte__ui.has_selection = 0;
  2909. break;
  2910. }
  2911. }
  2912. break;
  2913. case STBTE__tool_rect:
  2914. if (STBTE__IS_HOT(id)) {
  2915. switch (stbte__ui.event) {
  2916. case STBTE__leftdown:
  2917. if (STBTE__INACTIVE())
  2918. stbte__activate_map(mapx,mapy);
  2919. break;
  2920. case STBTE__leftup:
  2921. if (STBTE__IS_MAP_ACTIVE()) {
  2922. stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 1);
  2923. stbte__activate(0);
  2924. }
  2925. break;
  2926. case STBTE__rightdown:
  2927. if (STBTE__INACTIVE())
  2928. stbte__activate_map(mapx,mapy);
  2929. break;
  2930. case STBTE__rightup:
  2931. if (STBTE__IS_MAP_ACTIVE()) {
  2932. stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 0);
  2933. stbte__activate(0);
  2934. }
  2935. break;
  2936. }
  2937. }
  2938. break;
  2939. case STBTE__tool_eyedrop:
  2940. switch (stbte__ui.event) {
  2941. case STBTE__leftdown:
  2942. if (STBTE__IS_HOT(id) && STBTE__INACTIVE())
  2943. stbte__eyedrop(tm,mapx,mapy);
  2944. break;
  2945. }
  2946. break;
  2947. }
  2948. }
  2949. static void stbte__start_paste(stbte_tilemap *tm)
  2950. {
  2951. if (stbte__ui.has_copy) {
  2952. stbte__ui.pasting = 1;
  2953. stbte__activate(STBTE__ID(STBTE__toolbarB,3));
  2954. }
  2955. }
  2956. static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h)
  2957. {
  2958. int i;
  2959. int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4 - 30;
  2960. int x = x0 + w/2 - estimated_width/2;
  2961. int y = y0+1;
  2962. for (i=0; i < STBTE__num_tool; ++i) {
  2963. int highlight=0, disable=0;
  2964. highlight = (stbte__ui.tool == i);
  2965. if (i == STBTE__tool_undo || i == STBTE__tool_showgrid)
  2966. x += 8;
  2967. if (i == STBTE__tool_showgrid && stbte__ui.show_grid)
  2968. highlight = 1;
  2969. if (i == STBTE__tool_showlinks && stbte__ui.show_links)
  2970. highlight = 1;
  2971. if (i == STBTE__tool_fill)
  2972. continue;
  2973. #ifndef STBTE_ALLOW_LINK
  2974. if (i == STBTE__tool_link || i == STBTE__tool_showlinks)
  2975. disable = 1;
  2976. #endif
  2977. if (i == STBTE__tool_undo && !stbte__undo_available(tm))
  2978. disable = 1;
  2979. if (i == STBTE__tool_redo && !stbte__redo_available(tm))
  2980. disable = 1;
  2981. if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight, disable)) {
  2982. switch (i) {
  2983. case STBTE__tool_eyedrop:
  2984. stbte__ui.eyedrop_last_layer = tm->num_layers; // flush eyedropper state
  2985. // fallthrough
  2986. default:
  2987. stbte__ui.tool = i;
  2988. stbte__ui.has_selection = 0;
  2989. break;
  2990. case STBTE__tool_showlinks:
  2991. stbte__ui.show_links = !stbte__ui.show_links;
  2992. break;
  2993. case STBTE__tool_showgrid:
  2994. stbte__ui.show_grid = (stbte__ui.show_grid+1)%3;
  2995. break;
  2996. case STBTE__tool_undo:
  2997. stbte__undo(tm);
  2998. break;
  2999. case STBTE__tool_redo:
  3000. stbte__redo(tm);
  3001. break;
  3002. }
  3003. }
  3004. x += 13;
  3005. }
  3006. x += 8;
  3007. if (stbte__button(STBTE__ctoolbar_button, "cut" , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0, !stbte__ui.has_selection))
  3008. stbte__copy_cut(tm, 1);
  3009. x += 42;
  3010. if (stbte__button(STBTE__ctoolbar_button, "copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0, !stbte__ui.has_selection))
  3011. stbte__copy_cut(tm, 0);
  3012. x += 42;
  3013. if (stbte__button(STBTE__ctoolbar_button, "paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting, !stbte__ui.has_copy))
  3014. stbte__start_paste(tm);
  3015. }
  3016. #define STBTE__TEXTCOLOR(n) stbte__color_table[n][STBTE__text][STBTE__idle]
  3017. static int stbte__info_value(const char *label, int x, int y, int val, int digits, int id)
  3018. {
  3019. if (stbte__ui.event == STBTE__paint) {
  3020. int off = 9-stbte__get_char_width(label[0]);
  3021. char text[16];
  3022. sprintf(text, label, digits, val);
  3023. stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1);
  3024. }
  3025. if (id) {
  3026. x += 9+7*digits+4;
  3027. if (stbte__minibutton(STBTE__cmapsize, x,y, '+', STBTE__ID2(id,1,0)))
  3028. val += (stbte__ui.shift ? 10 : 1);
  3029. x += 9;
  3030. if (stbte__minibutton(STBTE__cmapsize, x,y, '-', STBTE__ID2(id,2,0)))
  3031. val -= (stbte__ui.shift ? 10 : 1);
  3032. if (val < 1) val = 1; else if (val > 4096) val = 4096;
  3033. }
  3034. return val;
  3035. }
  3036. static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h)
  3037. {
  3038. int mode = stbte__ui.panel[STBTE__panel_info].mode;
  3039. int s = 11+7*tm->digits+4+15;
  3040. int x,y;
  3041. int in_region;
  3042. x = x0+2;
  3043. y = y0+2;
  3044. tm->max_x = stbte__info_value("w:%*d",x,y, tm->max_x, tm->digits, STBTE__ID(STBTE__info,0));
  3045. if (mode)
  3046. x += s;
  3047. else
  3048. y += 11;
  3049. tm->max_y = stbte__info_value("h:%*d",x,y, tm->max_y, tm->digits, STBTE__ID(STBTE__info,1));
  3050. x = x0+2;
  3051. y += 11;
  3052. in_region = (stbte__ui.hot_id & 127) == STBTE__map;
  3053. stbte__info_value(in_region ? "x:%*d" : "x:",x,y, (stbte__ui.hot_id>>19)&4095, tm->digits, 0);
  3054. if (mode)
  3055. x += s;
  3056. else
  3057. y += 11;
  3058. stbte__info_value(in_region ? "y:%*d" : "y:",x,y, (stbte__ui.hot_id>> 7)&4095, tm->digits, 0);
  3059. y += 15;
  3060. x = x0+2;
  3061. stbte__draw_text(x,y,"brush:",40,STBTE__TEXTCOLOR(STBTE__cpanel));
  3062. if (tm->cur_tile >= 0)
  3063. STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1,0);
  3064. }
  3065. static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h)
  3066. {
  3067. static char *propmodes[3] = {
  3068. "default", "always", "never"
  3069. };
  3070. int num_rows;
  3071. int i, y, n;
  3072. int x1 = x0+w;
  3073. int y1 = y0+h;
  3074. int xoff = 20;
  3075. if (tm->has_layer_names) {
  3076. int side = stbte__ui.panel[STBTE__panel_layers].side;
  3077. xoff = stbte__region[side].width - 42;
  3078. xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10);
  3079. }
  3080. x0 += 2;
  3081. y0 += 5;
  3082. if (!tm->has_layer_names) {
  3083. if (stbte__ui.event == STBTE__paint) {
  3084. stbte__draw_text(x0,y0, "Layers", w-4, STBTE__TEXTCOLOR(STBTE__cpanel));
  3085. }
  3086. y0 += 11;
  3087. }
  3088. num_rows = (y1-y0)/15;
  3089. #ifndef STBTE_NO_PROPS
  3090. --num_rows;
  3091. #endif
  3092. y = y0;
  3093. for (i=0; i < tm->num_layers; ++i) {
  3094. char text[3], *str = (char *) tm->layerinfo[i].name;
  3095. static char lockedchar[3] = { 'U', 'P', 'L' };
  3096. int locked = tm->layerinfo[i].locked;
  3097. int disabled = (tm->solo_layer >= 0 && tm->solo_layer != i);
  3098. if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) {
  3099. if (str == NULL)
  3100. sprintf(str=text, "%2d", i+1);
  3101. if (stbte__button(STBTE__clayer_button, str, x0,y,(i+1<10)*2,xoff-2, STBTE__ID(STBTE__layer,i), tm->cur_layer==i,0))
  3102. tm->cur_layer = (tm->cur_layer == i ? -1 : i);
  3103. if (stbte__layerbutton(x0+xoff + 0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide))
  3104. tm->layerinfo[i].hidden = !tm->layerinfo[i].hidden;
  3105. if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE__clayer_lock))
  3106. tm->layerinfo[i].locked = (locked+1)%3;
  3107. if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE__clayer_solo))
  3108. tm->solo_layer = (tm->solo_layer == i ? -1 : i);
  3109. y += 15;
  3110. }
  3111. }
  3112. stbte__scrollbar(x1-4, y0,y-2, &tm->layer_scroll, 0, tm->num_layers, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__layer));
  3113. #ifndef STBTE_NO_PROPS
  3114. n = stbte__text_width("prop:")+2;
  3115. stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel));
  3116. i = w - n - 4;
  3117. if (i > 50) i = 50;
  3118. if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0))
  3119. tm->propmode = (tm->propmode+1)%3;
  3120. #endif
  3121. }
  3122. static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h)
  3123. {
  3124. int s=11, x,y, i;
  3125. int num_rows = h / s;
  3126. w -= 4;
  3127. x = x0+2;
  3128. y = y0+4;
  3129. if (tm->category_scroll == 0) {
  3130. if (stbte__category_button("*ALL*", x,y, w, STBTE__ID(STBTE__categories, 65535), tm->cur_category == -1)) {
  3131. stbte__choose_category(tm, -1);
  3132. }
  3133. y += s;
  3134. }
  3135. for (i=0; i < tm->num_categories; ++i) {
  3136. if (i+1 - tm->category_scroll >= 0 && i+1 - tm->category_scroll < num_rows) {
  3137. if (y + 10 > y0+h)
  3138. return;
  3139. if (stbte__category_button(tm->categories[i], x,y,w, STBTE__ID(STBTE__categories,i), tm->cur_category == i))
  3140. stbte__choose_category(tm, i);
  3141. y += s;
  3142. }
  3143. }
  3144. stbte__scrollbar(x0+w, y0+4, y0+h-4, &tm->category_scroll, 0, tm->num_categories+1, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__categories));
  3145. }
  3146. static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot)
  3147. {
  3148. stbte__tileinfo *t = &tm->tiles[slot];
  3149. int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y;
  3150. int id = STBTE__ID(STBTE__palette, slot);
  3151. int over = stbte__hittest(x0,y0,x1,y1, id);
  3152. switch (stbte__ui.event) {
  3153. case STBTE__paint:
  3154. stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND);
  3155. STBTE_DRAW_TILE(x,y,t->id, slot == tm->cur_tile,0);
  3156. if (slot == tm->cur_tile)
  3157. stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE);
  3158. break;
  3159. default:
  3160. if (stbte__button_core(id))
  3161. tm->cur_tile = slot;
  3162. break;
  3163. }
  3164. }
  3165. static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, int h)
  3166. {
  3167. int i,x,y;
  3168. int num_vis_rows = (h-6) / tm->palette_spacing_y;
  3169. int num_columns = (w-2-6) / tm->palette_spacing_x;
  3170. int num_total_rows;
  3171. int column,row;
  3172. int x1 = x0+w, y1=y0+h;
  3173. x = x0+2;
  3174. y = y0+6;
  3175. if (num_columns == 0)
  3176. return;
  3177. num_total_rows = (tm->cur_palette_count + num_columns-1) / num_columns; // ceil()
  3178. column = 0;
  3179. row = -tm->palette_scroll;
  3180. for (i=0; i < tm->num_tiles; ++i) {
  3181. stbte__tileinfo *t = &tm->tiles[i];
  3182. // filter based on category
  3183. if (tm->cur_category >= 0 && t->category_id != tm->cur_category)
  3184. continue;
  3185. // display it
  3186. if (row >= 0 && row < num_vis_rows) {
  3187. x = x0 + 2 + tm->palette_spacing_x * column;
  3188. y = y0 + 6 + tm->palette_spacing_y * row;
  3189. stbte__tile_in_palette(tm,x,y,i);
  3190. }
  3191. ++column;
  3192. if (column == num_columns) {
  3193. column = 0;
  3194. ++row;
  3195. }
  3196. }
  3197. stbte__flush_delay();
  3198. stbte__scrollbar(x1-4, y0+6, y1-2, &tm->palette_scroll, 0, num_total_rows, num_vis_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__palette));
  3199. }
  3200. static float stbte__linear_remap(float n, float x0, float x1, float y0, float y1)
  3201. {
  3202. return (n-x0)/(x1-x0)*(y1-y0) + y0;
  3203. }
  3204. static float stbte__saved;
  3205. static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
  3206. {
  3207. int x1 = x0+w, y1 = y0+h;
  3208. int i;
  3209. int y = y0 + 5, x = x0+2;
  3210. int slider_width = 60;
  3211. int mx,my;
  3212. float *p;
  3213. short *data;
  3214. if (!stbte__is_single_selection())
  3215. return;
  3216. mx = stbte__ui.select_x0;
  3217. my = stbte__ui.select_y0;
  3218. p = tm->props[my][mx];
  3219. data = tm->data[my][mx];
  3220. for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
  3221. unsigned int n = STBTE_PROP_TYPE(i, data, p);
  3222. if (n) {
  3223. char *s = STBTE_PROP_NAME(i, data, p);
  3224. if (s == NULL) s = "";
  3225. switch (n & 3) {
  3226. case STBTE_PROP_bool: {
  3227. int flag = (int) p[i];
  3228. if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) {
  3229. stbte__begin_undo(tm);
  3230. stbte__undo_record_prop_float(tm,mx,my,i,(float) flag);
  3231. p[i] = (float) !flag;
  3232. stbte__end_undo(tm);
  3233. }
  3234. stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel));
  3235. y += 13;
  3236. break;
  3237. }
  3238. case STBTE_PROP_int: {
  3239. int a = (int) STBTE_PROP_MIN(i,data,p);
  3240. int b = (int) STBTE_PROP_MAX(i,data,p);
  3241. int v = (int) p[i] - a;
  3242. if (a+v != p[i] || v < 0 || v > b-a) {
  3243. if (v < 0) v = 0;
  3244. if (v > b-a) v = b-a;
  3245. p[i] = (float) (a+v); // @TODO undo
  3246. }
  3247. switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i)))
  3248. {
  3249. case STBTE__begin:
  3250. stbte__saved = p[i];
  3251. // fallthrough
  3252. case STBTE__change:
  3253. p[i] = (float) (a+v); // @TODO undo
  3254. break;
  3255. case STBTE__end:
  3256. if (p[i] != stbte__saved) {
  3257. stbte__begin_undo(tm);
  3258. stbte__undo_record_prop_float(tm,mx,my,i,stbte__saved);
  3259. stbte__end_undo(tm);
  3260. }
  3261. break;
  3262. }
  3263. stbte__draw_text(x+slider_width+2,y+2, s, x1-1-(x+slider_width+2), STBTE__TEXTCOLOR(STBTE__cpanel));
  3264. y += 12;
  3265. break;
  3266. }
  3267. case STBTE_PROP_float: {
  3268. float a = (float) STBTE_PROP_MIN(i, data,p);
  3269. float b = (float) STBTE_PROP_MAX(i, data,p);
  3270. float c = STBTE_PROP_FLOAT_SCALE(i, data, p);
  3271. float old;
  3272. if (p[i] < a || p[i] > b) {
  3273. // @TODO undo
  3274. if (p[i] < a) p[i] = a;
  3275. if (p[i] > b) p[i] = b;
  3276. }
  3277. old = p[i];
  3278. switch (stbte__float_control(x, y, 50, a, b, c, "%8.4f", &p[i], STBTE__layer,STBTE__ID(STBTE__prop_float,i))) {
  3279. case STBTE__begin:
  3280. stbte__saved = old;
  3281. break;
  3282. case STBTE__end:
  3283. if (stbte__saved != p[i]) {
  3284. stbte__begin_undo(tm);
  3285. stbte__undo_record_prop_float(tm,mx,my,i, stbte__saved);
  3286. stbte__end_undo(tm);
  3287. }
  3288. break;
  3289. }
  3290. stbte__draw_text(x+53,y+1, s, x1-1-(x+53), STBTE__TEXTCOLOR(STBTE__cpanel));
  3291. y += 12;
  3292. break;
  3293. }
  3294. }
  3295. }
  3296. }
  3297. }
  3298. static int stbte__cp_mode, stbte__cp_aspect, stbte__cp_state, stbte__cp_index, stbte__save, stbte__cp_altered, stbte__color_copy;
  3299. #ifdef STBTE__COLORPICKER
  3300. static void stbte__dump_colorstate(void)
  3301. {
  3302. int i,j,k;
  3303. printf("static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =\n");
  3304. printf("{\n");
  3305. printf(" {\n");
  3306. for (k=0; k < STBTE__num_color_modes; ++k) {
  3307. for (j=0; j < STBTE__num_color_aspects; ++j) {
  3308. printf(" { ");
  3309. for (i=0; i < STBTE__num_color_states; ++i) {
  3310. printf("0x%06x, ", stbte__color_table[k][j][i]);
  3311. }
  3312. printf("},\n");
  3313. }
  3314. if (k+1 < STBTE__num_color_modes)
  3315. printf(" }, {\n");
  3316. else
  3317. printf(" },\n");
  3318. }
  3319. printf("};\n");
  3320. }
  3321. static void stbte__colorpicker(int x0, int y0, int w, int h)
  3322. {
  3323. int x1 = x0+w, y1 = y0+h, x,y, i;
  3324. x = x0+2; y = y0+6;
  3325. y += 5;
  3326. x += 8;
  3327. {
  3328. int color = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index];
  3329. int rgb[3];
  3330. if (stbte__cp_altered && stbte__cp_index == STBTE__idle)
  3331. color = stbte__save;
  3332. if (stbte__minibutton(STBTE__cmapsize, x1-20,y+ 5, 'C', STBTE__ID2(STBTE__colorpick_id,4,0)))
  3333. stbte__color_copy = color;
  3334. if (stbte__minibutton(STBTE__cmapsize, x1-20,y+15, 'P', STBTE__ID2(STBTE__colorpick_id,4,1)))
  3335. color = stbte__color_copy;
  3336. rgb[0] = color >> 16; rgb[1] = (color>>8)&255; rgb[2] = color & 255;
  3337. for (i=0; i < 3; ++i) {
  3338. if (stbte__slider(x+8,64, y, 255, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)) > 0)
  3339. stbte__dump_colorstate();
  3340. y += 15;
  3341. }
  3342. if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick)
  3343. stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index] = (rgb[0]<<16)|(rgb[1]<<8)|(rgb[2]);
  3344. }
  3345. y += 5;
  3346. // states
  3347. x = x0+2+35;
  3348. if (stbte__ui.event == STBTE__paint) {
  3349. static char *states[] = { "idle", "over", "down", "down&over", "selected", "selected&over", "disabled" };
  3350. stbte__draw_text(x, y+1, states[stbte__cp_index], x1-x-1, 0xffffff);
  3351. }
  3352. x = x0+24; y += 12;
  3353. for (i=3; i >= 0; --i) {
  3354. int state = 0 != (stbte__cp_state & (1 << i));
  3355. if (stbte__layerbutton(x,y, "OASD"[i], STBTE__ID2(STBTE__colorpick_id, 0,i), state,0, STBTE__clayer_button)) {
  3356. stbte__cp_state ^= (1 << i);
  3357. stbte__cp_index = stbte__state_to_index[0][0][0][stbte__cp_state];
  3358. }
  3359. x += 16;
  3360. }
  3361. x = x0+2; y += 18;
  3362. for (i=0; i < 3; ++i) {
  3363. static char *labels[] = { "Base", "Edge", "Text" };
  3364. if (stbte__button(STBTE__ctoolbar_button, labels[i], x,y,0,36, STBTE__ID2(STBTE__colorpick_id,1,i), stbte__cp_aspect==i,0))
  3365. stbte__cp_aspect = i;
  3366. x += 40;
  3367. }
  3368. y += 18;
  3369. x = x0+2;
  3370. for (i=0; i < STBTE__num_color_modes; ++i) {
  3371. if (stbte__button(STBTE__ctoolbar_button, stbte__color_names[i], x, y, 0,80, STBTE__ID2(STBTE__colorpick_id,2,i), stbte__cp_mode == i,0))
  3372. stbte__cp_mode = i;
  3373. y += 12;
  3374. }
  3375. // make the currently selected aspect flash, unless we're actively dragging color slider etc
  3376. if (stbte__ui.event == STBTE__tick) {
  3377. stbte__save = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle];
  3378. if ((stbte__ui.active_id & 127) != STBTE__colorpick_id) {
  3379. if ((stbte__ui.ms_time & 2047) < 200) {
  3380. stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] ^= 0x1f1f1f;
  3381. stbte__cp_altered = 1;
  3382. }
  3383. }
  3384. }
  3385. }
  3386. #endif
  3387. static void stbte__editor_traverse(stbte_tilemap *tm)
  3388. {
  3389. int i,j,i0,j0,i1,j1,n;
  3390. if (tm == NULL)
  3391. return;
  3392. if (stbte__ui.x0 == stbte__ui.x1 || stbte__ui.y0 == stbte__ui.y1)
  3393. return;
  3394. stbte__prepare_tileinfo(tm);
  3395. stbte__compute_panel_locations(tm); // @OPTIMIZE: we don't need to recompute this every time
  3396. if (stbte__ui.event == STBTE__paint) {
  3397. // fill screen with border
  3398. stbte__draw_rect(stbte__ui.x0, stbte__ui.y0, stbte__ui.x1, stbte__ui.y1, STBTE_COLOR_TILEMAP_BORDER);
  3399. // fill tilemap with tilemap background
  3400. stbte__draw_rect(stbte__ui.x0 - tm->scroll_x, stbte__ui.y0 - tm->scroll_y,
  3401. stbte__ui.x0 - tm->scroll_x + tm->spacing_x * tm->max_x,
  3402. stbte__ui.y0 - tm->scroll_y + tm->spacing_y * tm->max_y, STBTE_COLOR_TILEMAP_BACKGROUND);
  3403. }
  3404. // step 1: traverse all the tilemap data...
  3405. i0 = (tm->scroll_x - tm->spacing_x) / tm->spacing_x;
  3406. j0 = (tm->scroll_y - tm->spacing_y) / tm->spacing_y;
  3407. i1 = (tm->scroll_x + stbte__ui.x1 - stbte__ui.x0) / tm->spacing_x + 1;
  3408. j1 = (tm->scroll_y + stbte__ui.y1 - stbte__ui.y0) / tm->spacing_y + 1;
  3409. if (i0 < 0) i0 = 0;
  3410. if (j0 < 0) j0 = 0;
  3411. if (i1 > tm->max_x) i1 = tm->max_x;
  3412. if (j1 > tm->max_y) j1 = tm->max_y;
  3413. if (stbte__ui.event == STBTE__paint) {
  3414. // draw all of layer 0, then all of layer 1, etc, instead of old
  3415. // way which drew entire stack of each tile at once
  3416. for (n=0; n < tm->num_layers; ++n) {
  3417. for (j=j0; j < j1; ++j) {
  3418. for (i=i0; i < i1; ++i) {
  3419. int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x;
  3420. int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y;
  3421. stbte__tile_paint(tm, x, y, i, j, n);
  3422. }
  3423. }
  3424. if (n == 0 && stbte__ui.show_grid == 1) {
  3425. int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x;
  3426. int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y;
  3427. for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x)
  3428. stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID);
  3429. for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y)
  3430. stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID);
  3431. }
  3432. }
  3433. }
  3434. if (stbte__ui.event == STBTE__paint) {
  3435. // draw grid on top of everything except UI
  3436. if (stbte__ui.show_grid == 2) {
  3437. int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x;
  3438. int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y;
  3439. for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x)
  3440. stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID);
  3441. for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y)
  3442. stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID);
  3443. }
  3444. }
  3445. for (j=j0; j < j1; ++j) {
  3446. for (i=i0; i < i1; ++i) {
  3447. int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x;
  3448. int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y;
  3449. stbte__tile(tm, x, y, i, j);
  3450. }
  3451. }
  3452. if (stbte__ui.event == STBTE__paint) {
  3453. // draw the selection border
  3454. if (stbte__ui.has_selection) {
  3455. int x0,y0,x1,y1;
  3456. x0 = stbte__ui.x0 + (stbte__ui.select_x0 ) * tm->spacing_x - tm->scroll_x;
  3457. y0 = stbte__ui.y0 + (stbte__ui.select_y0 ) * tm->spacing_y - tm->scroll_y;
  3458. x1 = stbte__ui.x0 + (stbte__ui.select_x1 + 1) * tm->spacing_x - tm->scroll_x + 1;
  3459. y1 = stbte__ui.y0 + (stbte__ui.select_y1 + 1) * tm->spacing_y - tm->scroll_y + 1;
  3460. stbte__draw_frame(x0,y0,x1,y1, (stbte__ui.ms_time & 256 ? STBTE_COLOR_SELECTION_OUTLINE1 : STBTE_COLOR_SELECTION_OUTLINE2));
  3461. }
  3462. stbte__flush_delay(); // draw a dynamic link on top of the queued links
  3463. #ifdef STBTE_ALLOW_LINK
  3464. if (stbte__ui.linking && STBTE__IS_MAP_HOT()) {
  3465. int x0,y0,x1,y1;
  3466. int color;
  3467. int ex = ((stbte__ui.hot_id >> 19) & 4095);
  3468. int ey = ((stbte__ui.hot_id >> 7) & 4095);
  3469. x0 = stbte__ui.x0 + (stbte__ui.sx ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)+1;
  3470. y0 = stbte__ui.y0 + (stbte__ui.sy ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)+1;
  3471. x1 = stbte__ui.x0 + (ex ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)-1;
  3472. y1 = stbte__ui.y0 + (ey ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)-1;
  3473. if (STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx], tm->data[ey][ex], tm->props[ey][ex]))
  3474. color = STBTE_LINK_COLOR_DRAWING;
  3475. else
  3476. color = STBTE_LINK_COLOR_DISALLOWED;
  3477. stbte__draw_link(x0,y0,x1,y1, color);
  3478. }
  3479. #endif
  3480. }
  3481. stbte__flush_delay();
  3482. // step 2: traverse the panels
  3483. for (i=0; i < STBTE__num_panel; ++i) {
  3484. stbte__panel *p = &stbte__ui.panel[i];
  3485. if (stbte__ui.event == STBTE__paint) {
  3486. stbte__draw_box(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__cpanel, STBTE__idle);
  3487. }
  3488. // obscure tilemap data underneath panel
  3489. stbte__hittest(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__ID2(STBTE__panel, i, 0));
  3490. switch (i) {
  3491. case STBTE__panel_toolbar:
  3492. if (stbte__ui.event == STBTE__paint)
  3493. stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, stbte__color_table[STBTE__ctoolbar][STBTE__base][STBTE__idle]);
  3494. stbte__toolbar(tm,p->x0,p->y0,p->width,p->height);
  3495. break;
  3496. case STBTE__panel_info:
  3497. stbte__info(tm,p->x0,p->y0,p->width,p->height);
  3498. break;
  3499. case STBTE__panel_layers:
  3500. stbte__layers(tm,p->x0,p->y0,p->width,p->height);
  3501. break;
  3502. case STBTE__panel_categories:
  3503. stbte__categories(tm,p->x0,p->y0,p->width,p->height);
  3504. break;
  3505. case STBTE__panel_colorpick:
  3506. #ifdef STBTE__COLORPICKER
  3507. stbte__colorpicker(p->x0,p->y0,p->width,p->height);
  3508. #endif
  3509. break;
  3510. case STBTE__panel_tiles:
  3511. // erase boundary between categories and tiles if they're on same side
  3512. if (stbte__ui.event == STBTE__paint && p->side == stbte__ui.panel[STBTE__panel_categories].side)
  3513. stbte__draw_rect(p->x0+1,p->y0-1,p->x0+p->width-1,p->y0+1, stbte__color_table[STBTE__cpanel][STBTE__base][STBTE__idle]);
  3514. stbte__palette_of_tiles(tm,p->x0,p->y0,p->width,p->height);
  3515. break;
  3516. case STBTE__panel_props:
  3517. stbte__props_panel(tm,p->x0,p->y0,p->width,p->height);
  3518. break;
  3519. }
  3520. // draw the panel side selectors
  3521. for (j=0; j < 2; ++j) {
  3522. int result;
  3523. if (i == STBTE__panel_toolbar) continue;
  3524. result = stbte__microbutton(p->x0+p->width - 1 - 2*4 + 4*j,p->y0+2,3, STBTE__ID2(STBTE__panel, i, j+1), STBTE__cpanel_sider+j);
  3525. if (result) {
  3526. switch (j) {
  3527. case 0: p->side = result > 0 ? STBTE__side_left : STBTE__side_right; break;
  3528. case 1: p->delta_height += result; break;
  3529. }
  3530. }
  3531. }
  3532. }
  3533. if (stbte__ui.panel[STBTE__panel_categories].delta_height < -5) stbte__ui.panel[STBTE__panel_categories].delta_height = -5;
  3534. if (stbte__ui.panel[STBTE__panel_layers ].delta_height < -5) stbte__ui.panel[STBTE__panel_layers ].delta_height = -5;
  3535. // step 3: traverse the regions to place expander controls on them
  3536. for (i=0; i < 2; ++i) {
  3537. if (stbte__region[i].active) {
  3538. int x = stbte__region[i].x;
  3539. int width;
  3540. if (i == STBTE__side_left)
  3541. width = stbte__ui.left_width , x += stbte__region[i].width + 1;
  3542. else
  3543. width = -stbte__ui.right_width, x -= 6;
  3544. if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), &width)) {
  3545. // if non-0, it is expanding, so retract it
  3546. if (stbte__region[i].retracted == 0.0)
  3547. stbte__region[i].retracted = 0.01f;
  3548. else
  3549. stbte__region[i].retracted = 0.0;
  3550. }
  3551. if (i == STBTE__side_left)
  3552. stbte__ui.left_width = width;
  3553. else
  3554. stbte__ui.right_width = -width;
  3555. if (stbte__ui.event == STBTE__tick) {
  3556. if (stbte__region[i].retracted && stbte__region[i].retracted < 1.0f) {
  3557. stbte__region[i].retracted += stbte__ui.dt*4;
  3558. if (stbte__region[i].retracted > 1)
  3559. stbte__region[i].retracted = 1;
  3560. }
  3561. }
  3562. }
  3563. }
  3564. if (stbte__ui.event == STBTE__paint && stbte__ui.alert_msg) {
  3565. int w = stbte__text_width(stbte__ui.alert_msg);
  3566. int x = (stbte__ui.x0+stbte__ui.x1)/2;
  3567. int y = (stbte__ui.y0+stbte__ui.y1)*5/6;
  3568. stbte__draw_rect (x-w/2-4,y-8, x+w/2+4,y+8, 0x604020);
  3569. stbte__draw_frame(x-w/2-4,y-8, x+w/2+4,y+8, 0x906030);
  3570. stbte__draw_text (x-w/2,y-4, stbte__ui.alert_msg, w+1, 0xff8040);
  3571. }
  3572. #ifdef STBTE_SHOW_CURSOR
  3573. if (stbte__ui.event == STBTE__paint)
  3574. stbte__draw_bitmap(stbte__ui.mx, stbte__ui.my, stbte__get_char_width(26), stbte__get_char_bitmap(26), 0xe0e0e0);
  3575. #endif
  3576. if (stbte__ui.event == STBTE__tick && stbte__ui.alert_msg) {
  3577. stbte__ui.alert_timer -= stbte__ui.dt;
  3578. if (stbte__ui.alert_timer < 0) {
  3579. stbte__ui.alert_timer = 0;
  3580. stbte__ui.alert_msg = 0;
  3581. }
  3582. }
  3583. if (stbte__ui.event == STBTE__paint) {
  3584. stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] = stbte__save;
  3585. stbte__cp_altered = 0;
  3586. }
  3587. }
  3588. static void stbte__do_event(stbte_tilemap *tm)
  3589. {
  3590. stbte__ui.next_hot_id = 0;
  3591. stbte__editor_traverse(tm);
  3592. stbte__ui.hot_id = stbte__ui.next_hot_id;
  3593. // automatically cancel on mouse-up in case the object that triggered it
  3594. // doesn't exist anymore
  3595. if (stbte__ui.active_id) {
  3596. if (stbte__ui.event == STBTE__leftup || stbte__ui.event == STBTE__rightup) {
  3597. if (!stbte__ui.pasting) {
  3598. stbte__activate(0);
  3599. if (stbte__ui.undoing)
  3600. stbte__end_undo(tm);
  3601. stbte__ui.scrolling = 0;
  3602. stbte__ui.dragging = 0;
  3603. stbte__ui.linking = 0;
  3604. }
  3605. }
  3606. }
  3607. // we could do this stuff in the widgets directly, but it would keep recomputing
  3608. // the same thing on every tile, which seems dumb.
  3609. if (stbte__ui.pasting) {
  3610. if (STBTE__IS_MAP_HOT()) {
  3611. // compute pasting location based on last hot
  3612. stbte__ui.paste_x = ((stbte__ui.hot_id >> 19) & 4095) - (stbte__ui.copy_width >> 1);
  3613. stbte__ui.paste_y = ((stbte__ui.hot_id >> 7) & 4095) - (stbte__ui.copy_height >> 1);
  3614. }
  3615. }
  3616. if (stbte__ui.dragging) {
  3617. if (STBTE__IS_MAP_HOT()) {
  3618. stbte__ui.drag_dest_x = ((stbte__ui.hot_id >> 19) & 4095) - stbte__ui.drag_offx;
  3619. stbte__ui.drag_dest_y = ((stbte__ui.hot_id >> 7) & 4095) - stbte__ui.drag_offy;
  3620. }
  3621. }
  3622. }
  3623. static void stbte__set_event(int event, int x, int y)
  3624. {
  3625. stbte__ui.event = event;
  3626. stbte__ui.mx = x;
  3627. stbte__ui.my = y;
  3628. stbte__ui.dx = x - stbte__ui.last_mouse_x;
  3629. stbte__ui.dy = y - stbte__ui.last_mouse_y;
  3630. stbte__ui.last_mouse_x = x;
  3631. stbte__ui.last_mouse_y = y;
  3632. stbte__ui.accum_x += stbte__ui.dx;
  3633. stbte__ui.accum_y += stbte__ui.dy;
  3634. }
  3635. void stbte_draw(stbte_tilemap *tm)
  3636. {
  3637. stbte__ui.event = STBTE__paint;
  3638. stbte__editor_traverse(tm);
  3639. }
  3640. void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey)
  3641. {
  3642. stbte__set_event(STBTE__mousemove, x,y);
  3643. stbte__ui.shift = shifted;
  3644. stbte__ui.scrollkey = scrollkey;
  3645. stbte__do_event(tm);
  3646. }
  3647. void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey)
  3648. {
  3649. static int events[2][2] = { { STBTE__leftup , STBTE__leftdown },
  3650. { STBTE__rightup, STBTE__rightdown } };
  3651. stbte__set_event(events[right][down], x,y);
  3652. stbte__ui.shift = shifted;
  3653. stbte__ui.scrollkey = scrollkey;
  3654. stbte__do_event(tm);
  3655. }
  3656. void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll)
  3657. {
  3658. // not implemented yet -- need different way of hittesting
  3659. }
  3660. void stbte_action(stbte_tilemap *tm, enum stbte_action act)
  3661. {
  3662. switch (act) {
  3663. case STBTE_tool_select: stbte__ui.tool = STBTE__tool_select; break;
  3664. case STBTE_tool_brush: stbte__ui.tool = STBTE__tool_brush; break;
  3665. case STBTE_tool_erase: stbte__ui.tool = STBTE__tool_erase; break;
  3666. case STBTE_tool_rectangle: stbte__ui.tool = STBTE__tool_rect; break;
  3667. case STBTE_tool_eyedropper: stbte__ui.tool = STBTE__tool_eyedrop; break;
  3668. case STBTE_tool_link: stbte__ui.tool = STBTE__tool_link; break;
  3669. case STBTE_act_toggle_grid: stbte__ui.show_grid = (stbte__ui.show_grid+1) % 3; break;
  3670. case STBTE_act_toggle_links: stbte__ui.show_links ^= 1; break;
  3671. case STBTE_act_undo: stbte__undo(tm); break;
  3672. case STBTE_act_redo: stbte__redo(tm); break;
  3673. case STBTE_act_cut: stbte__copy_cut(tm, 1); break;
  3674. case STBTE_act_copy: stbte__copy_cut(tm, 0); break;
  3675. case STBTE_act_paste: stbte__start_paste(tm); break;
  3676. case STBTE_scroll_left: tm->scroll_x -= tm->spacing_x; break;
  3677. case STBTE_scroll_right: tm->scroll_x += tm->spacing_x; break;
  3678. case STBTE_scroll_up: tm->scroll_y -= tm->spacing_y; break;
  3679. case STBTE_scroll_down: tm->scroll_y += tm->spacing_y; break;
  3680. }
  3681. }
  3682. void stbte_tick(stbte_tilemap *tm, float dt)
  3683. {
  3684. stbte__ui.event = STBTE__tick;
  3685. stbte__ui.dt = dt;
  3686. stbte__do_event(tm);
  3687. stbte__ui.ms_time += (int) (dt * 1024) + 1; // make sure if time is superfast it always updates a little
  3688. }
  3689. void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xs, float ys, int xo, int yo)
  3690. {
  3691. #ifdef _SDL_H
  3692. SDL_Event *event = (SDL_Event *) sdl_event;
  3693. SDL_Keymod km = SDL_GetModState();
  3694. int shift = (km & KMOD_LCTRL) || (km & KMOD_RCTRL);
  3695. int scrollkey = 0 != SDL_GetKeyboardState(NULL)[SDL_SCANCODE_SPACE];
  3696. switch (event->type) {
  3697. case SDL_MOUSEMOTION:
  3698. stbte_mouse_move(tm, (int) (xs*event->motion.x+xo), (int) (ys*event->motion.y+yo), shift, scrollkey);
  3699. break;
  3700. case SDL_MOUSEBUTTONUP:
  3701. stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 0, shift, scrollkey);
  3702. break;
  3703. case SDL_MOUSEBUTTONDOWN:
  3704. stbte_mouse_button(tm, (int) (xs*event->button.x+xo), (int) (ys*event->button.y+yo), event->button.button != SDL_BUTTON_LEFT, 1, shift, scrollkey);
  3705. break;
  3706. case SDL_MOUSEWHEEL:
  3707. stbte_mouse_wheel(tm, stbte__ui.mx, stbte__ui.my, event->wheel.y);
  3708. break;
  3709. }
  3710. #else
  3711. STBTE__NOTUSED(tm);
  3712. STBTE__NOTUSED(sdl_event);
  3713. STBTE__NOTUSED(xs);
  3714. STBTE__NOTUSED(ys);
  3715. STBTE__NOTUSED(xo);
  3716. STBTE__NOTUSED(yo);
  3717. #endif
  3718. }
  3719. #endif // STB_TILEMAP_EDITOR_IMPLEMENTATION
  3720. /*
  3721. ------------------------------------------------------------------------------
  3722. This software is available under 2 licenses -- choose whichever you prefer.
  3723. ------------------------------------------------------------------------------
  3724. ALTERNATIVE A - MIT License
  3725. Copyright (c) 2017 Sean Barrett
  3726. Permission is hereby granted, free of charge, to any person obtaining a copy of
  3727. this software and associated documentation files (the "Software"), to deal in
  3728. the Software without restriction, including without limitation the rights to
  3729. use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
  3730. of the Software, and to permit persons to whom the Software is furnished to do
  3731. so, subject to the following conditions:
  3732. The above copyright notice and this permission notice shall be included in all
  3733. copies or substantial portions of the Software.
  3734. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  3735. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  3736. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  3737. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  3738. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  3739. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  3740. SOFTWARE.
  3741. ------------------------------------------------------------------------------
  3742. ALTERNATIVE B - Public Domain (www.unlicense.org)
  3743. This is free and unencumbered software released into the public domain.
  3744. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
  3745. software, either in source code form or as a compiled binary, for any purpose,
  3746. commercial or non-commercial, and by any means.
  3747. In jurisdictions that recognize copyright laws, the author or authors of this
  3748. software dedicate any and all copyright interest in the software to the public
  3749. domain. We make this dedication for the benefit of the public at large and to
  3750. the detriment of our heirs and successors. We intend this dedication to be an
  3751. overt act of relinquishment in perpetuity of all present and future rights to
  3752. this software under copyright law.
  3753. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  3754. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  3755. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  3756. AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
  3757. ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  3758. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  3759. ------------------------------------------------------------------------------
  3760. */