stb_tilemap_editor.h 125 KB

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