123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188 |
- // stb_tilemap_editor.h - v0.42 - Sean Barrett - http://nothings.org/stb
- // placed in the public domain - not copyrighted - first released 2014-09
- //
- // Embeddable tilemap editor for C/C++
- //
- //
- // TABLE OF CONTENTS
- // FAQ
- // How to compile/use the library
- // Additional configuration macros
- // API documentation
- // Info on editing multiple levels
- // Revision history
- // Todo
- // Credits
- // License
- //
- //
- // FAQ
- //
- // Q: What counts as a tilemap for this library?
- //
- // A: An array of rectangles, where each rectangle contains a small
- // stack of images.
- //
- // Q: What are the limitations?
- //
- // A: Maps are limited to 4096x4096 in dimension.
- // Each map square can only contain a stack of at most 32 images.
- // A map can only use up to 32768 distinct image tiles.
- //
- // Q: How do I compile this?
- //
- // A: You need to #define several symbols before #including it, but only
- // in one file. This will cause all the function definitions to be
- // generated in that file. See the "HOW TO COMPILE" section.
- //
- // Q: What advantages does this have over a standalone editor?
- //
- // A: For one, you can integrate the editor into your game so you can
- // flip between editing and testing without even switching windows.
- // For another, you don't need an XML parser to get at the map data.
- //
- // Q: Can I live-edit my game maps?
- //
- // A: Not really, the editor keeps its own map representation.
- //
- // Q: How do I save and load maps?
- //
- // A: You have to do this yourself. The editor provides serialization
- // functions (get & set) for reading and writing the map it holds.
- // You can choose whatever format you want to store the map to on
- // disk; you just need to provide functions to convert. (For example,
- // I actually store the editor's map representation to disk basically
- // as-is; then I have a single function that converts from the editor
- // map representation to the game representation, which is used both
- // to go from editor-to-game and from loaded-map-to-game.)
- //
- // Q: I want to have tiles change appearance based on what's
- // adjacent, or other tile-display/substitution trickiness.
- //
- // A: You can do this when you convert from the editor's map
- // representation to the game representation, but there's
- // no way to show this live in the editor.
- //
- // Q: The editor appears to be put map location (0,0) at the top left?
- // I want to use a different coordinate system in my game (e.g. y
- // increasing upwards, or origin at the center).
- //
- // A: You can do this when you convert from the editor's map
- // representation to the game representation. (Don't forget to
- // translate link coordinates as well!)
- //
- // Q: The editor appears to put pixel (0,0) at the top left? I want
- // to use a different coordinate system in my game.
- //
- // A: The editor defines an "editor pixel coordinate system" with
- // (0,0) at the top left and requires you to display things in
- // that coordinate system. You can freely remap those coordinates
- // to anything you want on screen.
- //
- // Q: How do I scale the user interface?
- //
- // A: Since you do all the rendering, you can scale up all the rendering
- // calls that the library makes to you. If you do, (a) you need
- // to also scale up the mouse coordinates, and (b) you may want
- // to scale the map display back down so that you're only scaling
- // the UI and not everything. See the next question.
- //
- // Q: How do I scale the map display?
- //
- // A: Use stbte_set_spacing() to change the size that the map is displayed
- // at. Note that the "callbacks" to draw tiles are used for both drawing
- // the map and drawing the tile palette, so that callback may need to
- // draw at two different scales. You should choose the scales to match
- // You can tell them apart because the
- // tile palette gets NULL for the property pointer.
- //
- // Q: How does object editing work?
- //
- // A: One way to think of this is that in the editor, you're placing
- // spawners, not objects. Each spawner must be tile-aligned, because
- // it's only a tile editor. Each tile (stack of layers) gets
- // an associated set of properties, and it's up to you to
- // determine what properties should appear for a given tile,
- // based on e.g. the spawners that are in it.
- //
- // Q: How are properties themselves handled?
- //
- // A: All properties, regardless of UI behavior, are internally floats.
- // Each tile has an array of floats associated with it, which is
- // passed back to you when drawing the tiles so you can draw
- // objects appropriately modified by the properties.
- //
- // Q: What if I want to have two different objects/spawners in
- // one tile, both of which have their own properties?
- //
- // A: Make sure STBTE_MAX_PROPERTIES is large enough for the sum of
- // properties in both objects, and then you have to explicitly
- // map the property slot #s to the appropriate objects. They'll
- // still all appear in a single property panel; there's no way
- // to get multiple panels.
- //
- // Q: Can I do one-to-many linking?
- //
- // A: The library only supports one link per tile. However, you
- // can have multiple tiles all link to a single tile. So, you
- // can fake one-to-many linking by linking in the reverse
- // direction.
- //
- // Q: What if I have two objects in the same tile, and they each
- // need an independent link? Or I have two kinds of link associated
- // with a single object?
- //
- // A: There is no way to do this. (Unless you can reverse one link.)
- //
- // Q: How does cut & paste interact with object properties & links?
- //
- // A: Currently the library has no idea which properties or links
- // are associated with which layers of a tile. So currently, the
- // library will only copy properties & links if the layer panel
- // is set to allow all layers to be copied, OR if you set the
- // "props" in the layer panel to "always". Similarly, you can
- // set "props" to "none" so it will never copy.
- //
- // Q: What happens if the library gets a memory allocation failure
- // while I'm editing? Will I lose my work?
- //
- // A: The library allocates all editor memory when you create
- // the tilemap. It allocates a maximally-sized map and a
- // fixed-size undo buffer (and the fixed-size copy buffer
- // is static), and never allocates memory while it's running.
- // So it can't fail due to running out of memory.
- //
- // Q: What happens if the library crashes while I'm editing? Will
- // I lose my work?
- //
- // A: Yes. Save often.
- //
- //
- // HOW TO COMPILE
- //
- // This header file contains both the header file and the
- // implementation file in one. To create the implementation,
- // in one source file define a few symbols first and then
- // include this header:
- //
- // #define STB_TILEMAP_EDITOR_IMPLEMENTATION
- // // this triggers the implementation
- //
- // void STBTE_DRAW_RECT(int x0, int y0, int x1, int y1, unsigned int color);
- // // this must draw a filled rectangle (exclusive on right/bottom)
- // // color = (r<<16)|(g<<8)|(b)
- //
- // void STBTE_DRAW_TILE(int x0, int y0,
- // unsigned short id, int highlight, float *data);
- // // this draws the tile image identified by 'id' in one of several
- // // highlight modes (see STBTE_drawmode_* in the header section);
- // // if 'data' is NULL, it's drawing the tile in the palette; if 'data'
- // // is not NULL, it's drawing a tile on the map, and that is the data
- // // associated with that map tile
- //
- // #include "stb_tilemap_editor.h"
- //
- // Optionally you can define the following functions before the include;
- // note these must be macros (but they can just call a function) so
- // this library can #ifdef to detect if you've defined them:
- //
- // #define STBTE_PROP_TYPE(int n, short *tiledata, float *params) ...
- // // Returns the type of the n'th property of a given tile, which
- // // controls how it is edited. Legal types are:
- // // 0 /* no editable property in this slot */
- // // STBTE_PROP_int /* uses a slider to adjust value */
- // // STBTE_PROP_float /* uses a weird multi-axis control */
- // // STBTE_PROP_bool /* uses a checkbox to change value */
- // // And you can bitwise-OR in the following flags:
- // // STBTE_PROP_disabled
- // // Note that all of these are stored as floats in the param array.
- // // The integer slider is limited in precision based on the space
- // // available on screen, so for wide-ranged integers you may want
- // // to use floats instead.
- // //
- // // Since the tiledata is passed to you, you can choose which property
- // // is bound to that slot based on that data.
- // //
- // // Changing the type of a parameter does not cause the underlying
- // // value to be clamped to the type min/max except when the tile is
- // // explicitly selected.
- //
- // #define STBTE_PROP_NAME(int n, short *tiledata, float *params) ...
- // // these return a string with the name for slot #n in the float
- // // property list for the tile.
- //
- // #define STBTE_PROP_MIN(int n, short *tiledata) ...your code here...
- // #define STBTE_PROP_MAX(int n, short *tiledata) ...your code here...
- // // These return the allowable range for the property values for
- // // the specified slot. It is never called for boolean types.
- //
- // #define STBTE_PROP_FLOAT_SCALE(int n, short *tiledata, float *params)
- // // This rescales the float control for a given property; by default
- // // left mouse drags add integers, right mouse drags adds fractions,
- // // but you can rescale this per-property.
- //
- // #define STBTE_FLOAT_CONTROL_GRANULARITY ... value ...
- // // This returns the number of pixels of mouse motion necessary
- // // to advance the object float control. Default is 4
- //
- // #define STBTE_ALLOW_LINK(short *src, float *src_data, \
- // short *dest, float *dest_data) ...your code...
- // // this returns true or false depending on whether you allow a link
- // // to be drawn from a tile 'src' to a tile 'dest'. if you don't
- // // define this, linking will not be supported
- //
- // #define STBTE_LINK_COLOR(short *src, float *src_data, \
- // short *dest, float *dest_data) ...your code...
- // // return a color encoded as a 24-bit unsigned integer in the
- // // form 0xRRGGBB. If you don't define this, default colors will
- // // be used.
- //
- //
- // [[ support for those below is not implemented yet ]]
- //
- // #define STBTE_HITTEST_TILE(x0,y0,id,mx,my) ...your code here...
- // // this returns true or false depending on whether the mouse
- // // pointer at mx,my is over (touching) a tile of type 'id'
- // // displayed at x0,y0. Normally stb_tilemap_editor just does
- // // this hittest based on the tile geometry, but if you have
- // // tiles whose images extend out of the tile, you'll need this.
- //
- // ADDITIONAL CONFIGURATION
- //
- // The following symbols set static limits which determine how much
- // memory will be allocated for the editor. You can override them
- // by making similar definitions, but memory usage will increase.
- //
- // #define STBTE_MAX_TILEMAP_X 200 // max 4096
- // #define STBTE_MAX_TILEMAP_Y 200 // max 4096
- // #define STBTE_MAX_LAYERS 8 // max 32
- // #define STBTE_MAX_CATEGORIES 100
- // #define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB
- // #define STBTE_MAX_COPY 90000 // e.g. 300x300
- // #define STBTE_MAX_PROPERTIES 10 // max properties per tile
- //
- // API
- //
- // Further documentation appears in the header-file section below.
- //
- // EDITING MULTIPLE LEVELS
- //
- // You can only have one active editor instance. To switch between multiple
- // levels, you can either store the levels in your own format and copy them
- // in and out of the editor format, or you can create multiple stbte_tilemap
- // objects and switch between them. The latter has the advantage that each
- // stbte_tilemap keeps its own undo state. (The clipboard is global, so
- // either approach allows cut&pasting between levels.)
- //
- // REVISION HISTORY
- // 0.42 fix compilation errors
- // 0.41 fix warnings
- // 0.40 fix warning
- // 0.39 fix warning
- // 0.38 fix warning
- // 0.37 fix warning
- // 0.36 minor compiler support
- // 0.35 layername button changes
- // - layername buttons grow with the layer panel
- // - fix stbte_create_map being declared as stbte_create
- // - fix declaration of stbte_create_map
- // 0.30 properties release
- // - properties panel for editing user-defined "object" properties
- // - can link each tile to one other tile
- // - keyboard interface
- // - fix eraser tool bug (worked in complex cases, failed in simple)
- // - undo/redo tools have visible disabled state
- // - tiles on higher layers draw on top of adjacent lower-layer tiles
- // 0.20 erasable release
- // - eraser tool
- // - fix bug when pasting into protected layer
- // - better color scheme
- // - internal-use color picker
- // 0.10 initial release
- //
- // TODO
- //
- // Separate scroll state for each category
- // Implement paint bucket
- // Support STBTE_HITTEST_TILE above
- // ?Cancel drags by clicking other button? - may be fixed
- // Finish support for toolbar at side
- //
- // CREDITS
- //
- //
- // Main editor & features
- // Sean Barrett
- // Additional features:
- // Josh Huelsman
- // Bugfixes:
- // Ryan Whitworth
- // Eugene Opalev
- // Rob Loach
- // github:wernsey
- //
- // LICENSE
- //
- // See end of file for license information.
- ///////////////////////////////////////////////////////////////////////
- //
- // HEADER SECTION
- #ifndef STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
- #define STB_TILEMAP_INCLUDE_STB_TILEMAP_EDITOR_H
- #ifdef _WIN32
- #ifndef _CRT_SECURE_NO_WARNINGS
- #define _CRT_SECURE_NO_WARNINGS
- #endif
- #include <stdlib.h>
- #include <stdio.h>
- #endif
- typedef struct stbte_tilemap stbte_tilemap;
- // these are the drawmodes used in STBTE_DRAW_TILE
- enum
- {
- STBTE_drawmode_deemphasize = -1,
- STBTE_drawmode_normal = 0,
- STBTE_drawmode_emphasize = 1,
- };
- // these are the property types
- #define STBTE_PROP_none 0
- #define STBTE_PROP_int 1
- #define STBTE_PROP_float 2
- #define STBTE_PROP_bool 3
- #define STBTE_PROP_disabled 4
- ////////
- //
- // creation
- //
- extern stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles);
- // create an editable tilemap
- // map_x : dimensions of map horizontally (user can change this in editor), <= STBTE_MAX_TILEMAP_X
- // map_y : dimensions of map vertically (user can change this in editor) <= STBTE_MAX_TILEMAP_Y
- // map_layers : number of layers to use (fixed), <= STBTE_MAX_LAYERS
- // spacing_x : initial horizontal distance between left edges of map tiles in stb_tilemap_editor pixels
- // spacing_y : initial vertical distance between top edges of map tiles in stb_tilemap_editor pixels
- // max_tiles : maximum number of tiles that can defined
- //
- // If insufficient memory, returns NULL
- extern void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category);
- // call this repeatedly for each tile to install the tile definitions into the editable tilemap
- // tm : tilemap created by stbte_create_map
- // id : unique identifier for each tile, 0 <= id < 32768
- // layermask : bitmask of which layers tile is allowed on: 1 = layer 0, 255 = layers 0..7
- // (note that onscreen, the editor numbers the layers from 1 not 0)
- // layer 0 is the furthest back, layer 1 is just in front of layer 0, etc
- // category : which category this tile is grouped in
- extern void stbte_set_display(int x0, int y0, int x1, int y1);
- // call this once to set the size; if you resize, call it again
- /////////
- //
- // every frame
- //
- extern void stbte_draw(stbte_tilemap *tm);
- extern void stbte_tick(stbte_tilemap *tm, float time_in_seconds_since_last_frame);
- ////////////
- //
- // user input
- //
- // if you're using SDL, call the next function for SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN, SDL_MOUSEBUTTONUP, SDL_MOUSEWHEEL;
- // the transformation lets you scale from SDL mouse coords to stb_tilemap_editor coords
- extern void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xscale, float yscale, int xoffset, int yoffset);
- // otherwise, hook these up explicitly:
- extern void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey);
- extern void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey);
- extern void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll);
- // note: at the moment, mouse wheel events (SDL_MOUSEWHEEL) are ignored.
- // for keyboard, define your own mapping from keys to the following actions.
- // this is totally optional, as all features are accessible with the mouse
- enum stbte_action
- {
- STBTE_tool_select,
- STBTE_tool_brush,
- STBTE_tool_erase,
- STBTE_tool_rectangle,
- STBTE_tool_eyedropper,
- STBTE_tool_link,
- STBTE_act_toggle_grid,
- STBTE_act_toggle_links,
- STBTE_act_undo,
- STBTE_act_redo,
- STBTE_act_cut,
- STBTE_act_copy,
- STBTE_act_paste,
- STBTE_scroll_left,
- STBTE_scroll_right,
- STBTE_scroll_up,
- STBTE_scroll_down,
- };
- extern void stbte_action(stbte_tilemap *tm, enum stbte_action act);
- ////////////////
- //
- // save/load
- //
- // There is no editor file format. You have to save and load the data yourself
- // through the following functions. You can also use these functions to get the
- // data to generate game-formatted levels directly. (But make sure you save
- // first! You may also want to autosave to a temp file periodically, etc etc.)
- #define STBTE_EMPTY -1
- extern void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y);
- // get the dimensions of the level, since the user can change them
- extern short* stbte_get_tile(stbte_tilemap *tm, int x, int y);
- // returns an array of shorts that is 'map_layers' in length. each short is
- // either one of the tile_id values from define_tile, or STBTE_EMPTY.
- extern float *stbte_get_properties(stbte_tilemap *tm, int x, int y);
- // get the property array associated with the tile at x,y. this is an
- // array of floats that is STBTE_MAX_PROPERTIES in length; you have to
- // interpret the slots according to the semantics you've chosen
- extern void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty);
- // gets the link associated with the tile at x,y.
- extern void stbte_set_dimensions(stbte_tilemap *tm, int max_x, int max_y);
- // set the dimensions of the level, overrides previous stbte_create_map()
- // values or anything the user has changed
- extern void stbte_clear_map(stbte_tilemap *tm);
- // clears the map, including the region outside the defined region, so if the
- // user expands the map, they won't see garbage there
- extern void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile);
- // tile is your tile_id from define_tile, or STBTE_EMPTY
- extern void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val);
- // set the value of the n'th slot of the tile at x,y
- extern void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty);
- // set a link going from x,y to destx,desty. to force no link,
- // use destx=desty=-1
- ////////
- //
- // optional
- //
- extern void stbte_set_background_tile(stbte_tilemap *tm, short id);
- // selects the tile to fill the bottom layer with and used to clear bottom tiles to;
- // should be same ID as
- extern void stbte_set_sidewidths(int left, int right);
- // call this once to set the left & right side widths. don't call
- // it again since the user can change it
- extern void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y);
- // call this to set the spacing of map tiles and the spacing of palette tiles.
- // if you rescale your display, call it again (e.g. you can implement map zooming yourself)
- extern void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername);
- // sets a string name for your layer that shows in the layer selector. note that this
- // makes the layer selector wider. 'layer' is from 0..(map_layers-1)
- #endif
- #ifdef STB_TILEMAP_EDITOR_IMPLEMENTATION
- #ifndef STBTE_ASSERT
- #define STBTE_ASSERT assert
- #include <assert.h>
- #endif
- #ifdef _MSC_VER
- #define STBTE__NOTUSED(v) (void)(v)
- #else
- #define STBTE__NOTUSED(v) (void)sizeof(v)
- #endif
- #ifndef STBTE_MAX_TILEMAP_X
- #define STBTE_MAX_TILEMAP_X 200
- #endif
- #ifndef STBTE_MAX_TILEMAP_Y
- #define STBTE_MAX_TILEMAP_Y 200
- #endif
- #ifndef STBTE_MAX_LAYERS
- #define STBTE_MAX_LAYERS 8
- #endif
- #ifndef STBTE_MAX_CATEGORIES
- #define STBTE_MAX_CATEGORIES 100
- #endif
- #ifndef STBTE_MAX_COPY
- #define STBTE_MAX_COPY 65536
- #endif
- #ifndef STBTE_UNDO_BUFFER_BYTES
- #define STBTE_UNDO_BUFFER_BYTES (1 << 24) // 16 MB
- #endif
- #ifndef STBTE_PROP_TYPE
- #define STBTE__NO_PROPS
- #define STBTE_PROP_TYPE(n,td,tp) 0
- #endif
- #ifndef STBTE_PROP_NAME
- #define STBTE_PROP_NAME(n,td,tp) ""
- #endif
- #ifndef STBTE_MAX_PROPERTIES
- #define STBTE_MAX_PROPERTIES 10
- #endif
- #ifndef STBTE_PROP_MIN
- #define STBTE_PROP_MIN(n,td,tp) 0
- #endif
- #ifndef STBTE_PROP_MAX
- #define STBTE_PROP_MAX(n,td,tp) 100.0
- #endif
- #ifndef STBTE_PROP_FLOAT_SCALE
- #define STBTE_PROP_FLOAT_SCALE(n,td,tp) 1 // default scale size
- #endif
- #ifndef STBTE_FLOAT_CONTROL_GRANULARITY
- #define STBTE_FLOAT_CONTROL_GRANULARITY 4
- #endif
- #define STBTE__UNDO_BUFFER_COUNT (STBTE_UNDO_BUFFER_BYTES>>1)
- #if STBTE_MAX_TILEMAP_X > 4096 || STBTE_MAX_TILEMAP_Y > 4096
- #error "Maximum editable map size is 4096 x 4096"
- #endif
- #if STBTE_MAX_LAYERS > 32
- #error "Maximum layers allowed is 32"
- #endif
- #if STBTE_UNDO_BUFFER_COUNT & (STBTE_UNDO_BUFFER_COUNT-1)
- #error "Undo buffer size must be a power of 2"
- #endif
- #if STBTE_MAX_PROPERTIES == 0
- #define STBTE__NO_PROPS
- #endif
- #ifdef STBTE__NO_PROPS
- #undef STBTE_MAX_PROPERTIES
- #define STBTE_MAX_PROPERTIES 1 // so we can declare arrays
- #endif
- typedef struct
- {
- short x,y;
- } stbte__link;
- enum
- {
- STBTE__base,
- STBTE__outline,
- STBTE__text,
- STBTE__num_color_aspects,
- };
- enum
- {
- STBTE__idle,
- STBTE__over,
- STBTE__down,
- STBTE__over_down,
- STBTE__selected,
- STBTE__selected_over,
- STBTE__disabled,
- STBTE__num_color_states,
- };
- enum
- {
- STBTE__cexpander,
- STBTE__ctoolbar,
- STBTE__ctoolbar_button,
- STBTE__cpanel,
- STBTE__cpanel_sider,
- STBTE__cpanel_sizer,
- STBTE__cscrollbar,
- STBTE__cmapsize,
- STBTE__clayer_button,
- STBTE__clayer_hide,
- STBTE__clayer_lock,
- STBTE__clayer_solo,
- STBTE__ccategory_button,
- STBTE__num_color_modes,
- };
- #ifdef STBTE__COLORPICKER
- static char *stbte__color_names[] =
- {
- "expander", "toolbar", "tool button", "panel",
- "panel c1", "panel c2", "scollbar", "map button",
- "layer", "hide", "lock", "solo",
- "category",
- };
- #endif // STBTE__COLORPICKER
- // idle, over, down, over&down, selected, sel&over, disabled
- static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =
- {
- {
- { 0x000000, 0x84987c, 0xdcdca8, 0xdcdca8, 0x40c040, 0x60d060, 0x505050, },
- { 0xa4b090, 0xe0ec80, 0xffffc0, 0xffffc0, 0x80ff80, 0x80ff80, 0x606060, },
- { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
- }, {
- { 0x808890, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, },
- { 0x605860, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, 0x606060, },
- { 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, 0x000000, },
- }, {
- { 0x3c5068, 0x7088a8, 0x647488, 0x94b4dc, 0x8890c4, 0x9caccc, 0x404040, },
- { 0x889cb8, 0x889cb8, 0x889cb8, 0x889cb8, 0x84c4e8, 0xacc8ff, 0x0c0c08, },
- { 0xbcc4cc, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x707074, },
- }, {
- { 0x403848, 0x403010, 0x403010, 0x403010, 0x403010, 0x403010, 0x303024, },
- { 0x68546c, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0xc08040, 0x605030, },
- { 0xf4e4ff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
- }, {
- { 0xb4b04c, 0xacac60, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, },
- { 0xa0a04c, 0xd0d04c, 0xffff80, 0xffff80, 0x80ff80, 0x80ff80, 0x606060, },
- { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
- }, {
- { 0x40c440, 0x60d060, 0xc0ffc0, 0xc0ffc0, 0x40c040, 0x60d060, 0x505050, },
- { 0x40c040, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x80ff80, 0x606060, },
- { 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
- }, {
- { 0x9090ac, 0xa0a0b8, 0xbcb8cc, 0xbcb8cc, 0x909040, 0x909040, 0x909040, },
- { 0xa0a0b8, 0xb0b4d0, 0xa0a0b8, 0xa0a0b8, 0xa0a050, 0xa0a050, 0xa0a050, },
- { 0x808088, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, 0x808030, },
- }, {
- { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, },
- { 0x646064, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, },
- { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
- }, {
- { 0x704c70, 0x885c8c, 0x9c68a4, 0xb870bc, 0xb490bc, 0xb490bc, 0x302828, },
- { 0xb09cb4, 0xcca8d4, 0xc060c0, 0xa07898, 0xe0b8e0, 0xe0b8e0, 0x403838, },
- { 0xdccce4, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0xffffff, 0x909090, },
- }, {
- { 0x646494, 0x888cb8, 0xb0b0b0, 0xb0b0cc, 0x9c9cf4, 0x8888b0, 0x50506c, },
- { 0x9090a4, 0xb0b4d4, 0xb0b0dc, 0xb0b0cc, 0xd0d0fc, 0xd0d4f0, 0x606060, },
- { 0xb4b4d4, 0xe4e4ff, 0xffffff, 0xffffff, 0xe0e4ff, 0xececff, 0x909090, },
- }, {
- { 0x646444, 0x888c64, 0xb0b0b0, 0xb0b088, 0xaca858, 0x88886c, 0x505050, },
- { 0x88886c, 0xb0b490, 0xb0b0b0, 0xb0b088, 0xd8d898, 0xd0d4b0, 0x606060, },
- { 0xb4b49c, 0xffffd8, 0xffffff, 0xffffd4, 0xffffdc, 0xffffcc, 0x909090, },
- }, {
- { 0x906464, 0xb48c8c, 0xd4b0b0, 0xdcb0b0, 0xff9c9c, 0xc88888, 0x505050, },
- { 0xb47c80, 0xd4b4b8, 0xc4a8a8, 0xdcb0b0, 0xffc0c0, 0xfce8ec, 0x606060, },
- { 0xe0b4b4, 0xffdcd8, 0xffd8d4, 0xffe0e4, 0xffece8, 0xffffff, 0x909090, },
- }, {
- { 0x403848, 0x403848, 0x403848, 0x886894, 0x7c80c8, 0x7c80c8, 0x302828, },
- { 0x403848, 0x403848, 0x403848, 0x403848, 0x7c80c8, 0x7c80c8, 0x403838, },
- { 0xc8c4c8, 0xffffff, 0xffffff, 0xffffff, 0xe8e8ec, 0xffffff, 0x909090, },
- },
- };
- #define STBTE_COLOR_TILEMAP_BACKGROUND 0x000000
- #define STBTE_COLOR_TILEMAP_BORDER 0x203060
- #define STBTE_COLOR_TILEMAP_HIGHLIGHT 0xffffff
- #define STBTE_COLOR_GRID 0x404040
- #define STBTE_COLOR_SELECTION_OUTLINE1 0xdfdfdf
- #define STBTE_COLOR_SELECTION_OUTLINE2 0x303030
- #define STBTE_COLOR_TILEPALETTE_OUTLINE 0xffffff
- #define STBTE_COLOR_TILEPALETTE_BACKGROUND 0x000000
- #ifndef STBTE_LINK_COLOR
- #define STBTE_LINK_COLOR(src,sp,dest,dp) 0x5030ff
- #endif
- #ifndef STBTE_LINK_COLOR_DRAWING
- #define STBTE_LINK_COLOR_DRAWING 0xff40ff
- #endif
- #ifndef STBTE_LINK_COLOR_DISALLOWED
- #define STBTE_LINK_COLOR_DISALLOWED 0x602060
- #endif
- // disabled, selected, down, over
- static unsigned char stbte__state_to_index[2][2][2][2] =
- {
- {
- { { STBTE__idle , STBTE__over }, { STBTE__down , STBTE__over_down }, },
- { { STBTE__selected, STBTE__selected_over }, { STBTE__down , STBTE__over_down }, },
- },{
- { { STBTE__disabled, STBTE__disabled }, { STBTE__disabled, STBTE__disabled }, },
- { { STBTE__selected, STBTE__selected_over }, { STBTE__disabled, STBTE__disabled }, },
- }
- };
- #define STBTE__INDEX_FOR_STATE(disable,select,down,over) stbte__state_to_index[disable][select][down][over]
- #define STBTE__INDEX_FOR_ID(id,disable,select) STBTE__INDEX_FOR_STATE(disable,select,STBTE__IS_ACTIVE(id),STBTE__IS_HOT(id))
- #define STBTE__FONT_HEIGHT 9
- static short stbte__font_offset[95+16];
- static short stbte__fontdata[769] =
- {
- 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,
- 6,6,6,6,6,2,3,5,4,5,6,6,6,6,6,6,6,6,6,6,6,6,7,6,7,7,7,6,7,6,6,6,6,7,7,6,6,
- 6,4,6,4,7,7,3,6,6,5,6,6,5,6,6,4,5,6,4,7,6,6,6,6,6,6,6,6,6,7,6,6,6,5,2,5,8,
- 0,0,0,0,2,253,130,456,156,8,72,184,64,2,125,66,64,160,64,146,511,146,146,
- 511,146,146,511,146,511,257,341,297,341,297,341,257,511,16,56,124,16,16,16,
- 124,56,16,96,144,270,261,262,136,80,48,224,192,160,80,40,22,14,15,3,448,496,
- 496,240,232,20,10,5,2,112,232,452,450,225,113,58,28,63,30,60,200,455,257,
- 257,0,0,0,257,257,455,120,204,132,132,159,14,4,4,14,159,132,132,204,120,8,
- 24,56,120,56,24,8,32,48,56,60,56,48,32,0,0,0,0,111,111,7,7,0,0,7,7,34,127,
- 127,34,34,127,127,34,36,46,107,107,58,18,99,51,24,12,102,99,48,122,79,93,
- 55,114,80,4,7,3,62,127,99,65,65,99,127,62,8,42,62,28,28,62,42,8,8,8,62,62,
- 8,8,128,224,96,8,8,8,8,8,8,96,96,96,48,24,12,6,3,62,127,89,77,127,62,64,66,
- 127,127,64,64,98,115,89,77,71,66,33,97,73,93,119,35,24,28,22,127,127,16,39,
- 103,69,69,125,57,62,127,73,73,121,48,1,1,113,121,15,7,54,127,73,73,127,54,
- 6,79,73,105,63,30,54,54,128,246,118,8,28,54,99,65,20,20,20,20,65,99,54,28,
- 8,2,3,105,109,7,2,30,63,33,45,47,46,124,126,19,19,126,124,127,127,73,73,127,
- 54,62,127,65,65,99,34,127,127,65,99,62,28,127,127,73,73,73,65,127,127,9,9,
- 9,1,62,127,65,73,121,121,127,127,8,8,127,127,65,65,127,127,65,65,32,96,64,
- 64,127,63,127,127,8,28,54,99,65,127,127,64,64,64,64,127,127,6,12,6,127,127,
- 127,127,6,12,24,127,127,62,127,65,65,65,127,62,127,127,9,9,15,6,62,127,65,
- 81,49,127,94,127,127,9,25,127,102,70,79,73,73,121,49,1,1,127,127,1,1,63,127,
- 64,64,127,63,15,31,48,96,48,31,15,127,127,48,24,48,127,127,99,119,28,28,119,
- 99,7,15,120,120,15,7,97,113,89,77,71,67,127,127,65,65,3,6,12,24,48,96,65,
- 65,127,127,8,12,6,3,6,12,8,64,64,64,64,64,64,64,3,7,4,32,116,84,84,124,120,
- 127,127,68,68,124,56,56,124,68,68,68,56,124,68,68,127,127,56,124,84,84,92,
- 24,8,124,126,10,10,56,380,324,324,508,252,127,127,4,4,124,120,72,122,122,
- 64,256,256,256,506,250,126,126,16,56,104,64,66,126,126,64,124,124,24,56,28,
- 124,120,124,124,4,4,124,120,56,124,68,68,124,56,508,508,68,68,124,56,56,124,
- 68,68,508,508,124,124,4,4,12,8,72,92,84,84,116,36,4,4,62,126,68,68,60,124,
- 64,64,124,124,28,60,96,96,60,28,28,124,112,56,112,124,28,68,108,56,56,108,
- 68,284,316,352,320,508,252,68,100,116,92,76,68,8,62,119,65,65,127,127,65,
- 65,119,62,8,16,24,12,12,24,24,12,4,
- };
- typedef struct
- {
- short id;
- unsigned short category_id;
- char *category;
- unsigned int layermask;
- } stbte__tileinfo;
- #define MAX_LAYERMASK (1 << (8*sizeof(unsigned int)))
- typedef short stbte__tiledata;
- #define STBTE__NO_TILE -1
- enum
- {
- STBTE__panel_toolbar,
- STBTE__panel_colorpick,
- STBTE__panel_info,
- STBTE__panel_layers,
- STBTE__panel_props,
- STBTE__panel_categories,
- STBTE__panel_tiles,
- STBTE__num_panel,
- };
- enum
- {
- STBTE__side_left,
- STBTE__side_right,
- STBTE__side_top,
- STBTE__side_bottom,
- };
- enum
- {
- STBTE__tool_select,
- STBTE__tool_brush,
- STBTE__tool_erase,
- STBTE__tool_rect,
- STBTE__tool_eyedrop,
- STBTE__tool_fill,
- STBTE__tool_link,
- STBTE__tool_showgrid,
- STBTE__tool_showlinks,
- STBTE__tool_undo,
- STBTE__tool_redo,
- // copy/cut/paste aren't included here because they're displayed differently
- STBTE__num_tool,
- };
- // icons are stored in the 0-31 range of ASCII in the font
- static int toolchar[] = { 26,24,25,20,23,22,18, 19,17, 29,28, };
- enum
- {
- STBTE__propmode_default,
- STBTE__propmode_always,
- STBTE__propmode_never,
- };
- enum
- {
- STBTE__paint,
- // from here down does hittesting
- STBTE__tick,
- STBTE__mousemove,
- STBTE__mousewheel,
- STBTE__leftdown,
- STBTE__leftup,
- STBTE__rightdown,
- STBTE__rightup,
- };
- typedef struct
- {
- int expanded, mode;
- int delta_height; // number of rows they've requested for this
- int side;
- int width,height;
- int x0,y0;
- } stbte__panel;
- typedef struct
- {
- int x0,y0,x1,y1,color;
- } stbte__colorrect;
- #define STBTE__MAX_DELAYRECT 256
- typedef struct
- {
- int tool, active_event;
- int active_id, hot_id, next_hot_id;
- int event;
- int mx,my, dx,dy;
- int ms_time;
- int shift, scrollkey;
- int initted;
- int side_extended[2];
- stbte__colorrect delayrect[STBTE__MAX_DELAYRECT];
- int delaycount;
- int show_grid, show_links;
- int brush_state; // used to decide which kind of erasing
- int eyedrop_x, eyedrop_y, eyedrop_last_layer;
- int pasting, paste_x, paste_y;
- int scrolling, start_x, start_y;
- int last_mouse_x, last_mouse_y;
- int accum_x, accum_y;
- int linking;
- int dragging;
- int drag_x, drag_y, drag_w, drag_h;
- int drag_offx, drag_offy, drag_dest_x, drag_dest_y;
- int undoing;
- int has_selection, select_x0, select_y0, select_x1, select_y1;
- int sx,sy;
- int x0,y0,x1,y1, left_width, right_width; // configurable widths
- float alert_timer;
- const char *alert_msg;
- float dt;
- stbte__panel panel[STBTE__num_panel];
- short copybuffer[STBTE_MAX_COPY][STBTE_MAX_LAYERS];
- float copyprops[STBTE_MAX_COPY][STBTE_MAX_PROPERTIES];
- #ifdef STBTE_ALLOW_LINK
- stbte__link copylinks[STBTE_MAX_COPY];
- #endif
- int copy_src_x, copy_src_y;
- stbte_tilemap *copy_src;
- int copy_width,copy_height,has_copy,copy_has_props;
- } stbte__ui_t;
- // there's only one UI system at a time, so we can globalize this
- static stbte__ui_t stbte__ui = { STBTE__tool_brush, 0 };
- #define STBTE__INACTIVE() (stbte__ui.active_id == 0)
- #define STBTE__IS_ACTIVE(id) (stbte__ui.active_id == (id))
- #define STBTE__IS_HOT(id) (stbte__ui.hot_id == (id))
- #define STBTE__BUTTON_HEIGHT (STBTE__FONT_HEIGHT + 2 * STBTE__BUTTON_INTERNAL_SPACING)
- #define STBTE__BUTTON_INTERNAL_SPACING (2 + (STBTE__FONT_HEIGHT>>4))
- typedef struct
- {
- const char *name;
- int locked;
- int hidden;
- } stbte__layer;
- enum
- {
- STBTE__unlocked,
- STBTE__protected,
- STBTE__locked,
- };
- struct stbte_tilemap
- {
- stbte__tiledata data[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_LAYERS];
- float props[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X][STBTE_MAX_PROPERTIES];
- #ifdef STBTE_ALLOW_LINK
- stbte__link link[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
- int linkcount[STBTE_MAX_TILEMAP_Y][STBTE_MAX_TILEMAP_X];
- #endif
- int max_x, max_y, num_layers;
- int spacing_x, spacing_y;
- int palette_spacing_x, palette_spacing_y;
- int scroll_x,scroll_y;
- int cur_category, cur_tile, cur_layer;
- char *categories[STBTE_MAX_CATEGORIES];
- int num_categories, category_scroll;
- stbte__tileinfo *tiles;
- int num_tiles, max_tiles, digits;
- unsigned char undo_available_valid;
- unsigned char undo_available;
- unsigned char redo_available;
- unsigned char padding;
- int cur_palette_count;
- int palette_scroll;
- int tileinfo_dirty;
- stbte__layer layerinfo[STBTE_MAX_LAYERS];
- int has_layer_names;
- int layername_width;
- int layer_scroll;
- int propmode;
- int solo_layer;
- int undo_pos, undo_len, redo_len;
- short background_tile;
- unsigned char id_in_use[32768>>3];
- short *undo_buffer;
- };
- static char *default_category = (char*) "[unassigned]";
- static void stbte__init_gui(void)
- {
- int i,n;
- stbte__ui.initted = 1;
- // init UI state
- stbte__ui.show_links = 1;
- for (i=0; i < STBTE__num_panel; ++i) {
- stbte__ui.panel[i].expanded = 1; // visible if not autohidden
- stbte__ui.panel[i].delta_height = 0;
- stbte__ui.panel[i].side = STBTE__side_left;
- }
- stbte__ui.panel[STBTE__panel_toolbar ].side = STBTE__side_top;
- stbte__ui.panel[STBTE__panel_colorpick].side = STBTE__side_right;
- if (stbte__ui.left_width == 0)
- stbte__ui.left_width = 80;
- if (stbte__ui.right_width == 0)
- stbte__ui.right_width = 80;
- // init font
- n=95+16;
- for (i=0; i < 95+16; ++i) {
- stbte__font_offset[i] = n;
- n += stbte__fontdata[i];
- }
- }
- stbte_tilemap *stbte_create_map(int map_x, int map_y, int map_layers, int spacing_x, int spacing_y, int max_tiles)
- {
- int i;
- stbte_tilemap *tm;
- STBTE_ASSERT(map_layers >= 0 && map_layers <= STBTE_MAX_LAYERS);
- STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
- STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
- if (map_x < 0 || map_y < 0 || map_layers < 0 ||
- map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y || map_layers > STBTE_MAX_LAYERS)
- return NULL;
- if (!stbte__ui.initted)
- stbte__init_gui();
- tm = (stbte_tilemap *) malloc(sizeof(*tm) + sizeof(*tm->tiles) * max_tiles + STBTE_UNDO_BUFFER_BYTES);
- if (tm == NULL)
- return NULL;
- tm->tiles = (stbte__tileinfo *) (tm+1);
- tm->undo_buffer = (short *) (tm->tiles + max_tiles);
- tm->num_layers = map_layers;
- tm->max_x = map_x;
- tm->max_y = map_y;
- tm->spacing_x = spacing_x;
- tm->spacing_y = spacing_y;
- tm->scroll_x = 0;
- tm->scroll_y = 0;
- tm->palette_scroll = 0;
- tm->palette_spacing_x = spacing_x+1;
- tm->palette_spacing_y = spacing_y+1;
- tm->cur_category = -1;
- tm->cur_tile = 0;
- tm->solo_layer = -1;
- tm->undo_len = 0;
- tm->redo_len = 0;
- tm->undo_pos = 0;
- tm->category_scroll = 0;
- tm->layer_scroll = 0;
- tm->propmode = 0;
- tm->has_layer_names = 0;
- tm->layername_width = 0;
- tm->undo_available_valid = 0;
- for (i=0; i < tm->num_layers; ++i) {
- tm->layerinfo[i].hidden = 0;
- tm->layerinfo[i].locked = STBTE__unlocked;
- tm->layerinfo[i].name = 0;
- }
- tm->background_tile = STBTE__NO_TILE;
- stbte_clear_map(tm);
- tm->max_tiles = max_tiles;
- tm->num_tiles = 0;
- for (i=0; i < 32768/8; ++i)
- tm->id_in_use[i] = 0;
- tm->tileinfo_dirty = 1;
- return tm;
- }
- void stbte_set_background_tile(stbte_tilemap *tm, short id)
- {
- int i;
- STBTE_ASSERT(id >= -1);
- // STBTE_ASSERT(id < 32768);
- if (id < -1)
- return;
- for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i)
- if (tm->data[0][i][0] == -1)
- tm->data[0][i][0] = id;
- tm->background_tile = id;
- }
- void stbte_set_spacing(stbte_tilemap *tm, int spacing_x, int spacing_y, int palette_spacing_x, int palette_spacing_y)
- {
- tm->spacing_x = spacing_x;
- tm->spacing_y = spacing_y;
- tm->palette_spacing_x = palette_spacing_x;
- tm->palette_spacing_y = palette_spacing_y;
- }
- void stbte_set_sidewidths(int left, int right)
- {
- stbte__ui.left_width = left;
- stbte__ui.right_width = right;
- }
- void stbte_set_display(int x0, int y0, int x1, int y1)
- {
- stbte__ui.x0 = x0;
- stbte__ui.y0 = y0;
- stbte__ui.x1 = x1;
- stbte__ui.y1 = y1;
- }
- void stbte_define_tile(stbte_tilemap *tm, unsigned short id, unsigned int layermask, const char * category_c)
- {
- char *category = (char *) category_c;
- STBTE_ASSERT(id < 32768);
- STBTE_ASSERT(tm->num_tiles < tm->max_tiles);
- STBTE_ASSERT((tm->id_in_use[id>>3]&(1<<(id&7))) == 0);
- if (id >= 32768 || tm->num_tiles >= tm->max_tiles || (tm->id_in_use[id>>3]&(1<<(id&7))))
- return;
- if (category == NULL)
- category = (char*) default_category;
- tm->id_in_use[id>>3] |= 1 << (id&7);
- tm->tiles[tm->num_tiles].category = category;
- tm->tiles[tm->num_tiles].id = id;
- tm->tiles[tm->num_tiles].layermask = layermask;
- ++tm->num_tiles;
- tm->tileinfo_dirty = 1;
- }
- static int stbte__text_width(const char *str);
- void stbte_set_layername(stbte_tilemap *tm, int layer, const char *layername)
- {
- STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
- if (layer >= 0 && layer < tm->num_layers) {
- int width;
- tm->layerinfo[layer].name = layername;
- tm->has_layer_names = 1;
- width = stbte__text_width(layername);
- tm->layername_width = (width > tm->layername_width ? width : tm->layername_width);
- }
- }
- void stbte_get_dimensions(stbte_tilemap *tm, int *max_x, int *max_y)
- {
- *max_x = tm->max_x;
- *max_y = tm->max_y;
- }
- short* stbte_get_tile(stbte_tilemap *tm, int x, int y)
- {
- STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
- if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
- return NULL;
- return tm->data[y][x];
- }
- float *stbte_get_properties(stbte_tilemap *tm, int x, int y)
- {
- STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
- if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
- return NULL;
- return tm->props[y][x];
- }
- void stbte_get_link(stbte_tilemap *tm, int x, int y, int *destx, int *desty)
- {
- int gx=-1,gy=-1;
- STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
- #ifdef STBTE_ALLOW_LINK
- if (x >= 0 && x < STBTE_MAX_TILEMAP_X && y >= 0 && y < STBTE_MAX_TILEMAP_Y) {
- gx = tm->link[y][x].x;
- gy = tm->link[y][x].y;
- if (gx >= 0)
- if (!STBTE_ALLOW_LINK(tm->data[y][x], tm->props[y][x], tm->data[gy][gx], tm->props[gy][gx]))
- gx = gy = -1;
- }
- #endif
- *destx = gx;
- *desty = gy;
- }
- void stbte_set_property(stbte_tilemap *tm, int x, int y, int n, float val)
- {
- tm->props[y][x][n] = val;
- }
- #ifdef STBTE_ALLOW_LINK
- static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode);
- #endif
- enum
- {
- STBTE__undo_none,
- STBTE__undo_record,
- STBTE__undo_block,
- };
- void stbte_set_link(stbte_tilemap *tm, int x, int y, int destx, int desty)
- {
- #ifdef STBTE_ALLOW_LINK
- stbte__set_link(tm, x, y, destx, desty, STBTE__undo_none);
- #else
- STBTE_ASSERT(0);
- #endif
- }
- // returns an array of map_layers shorts. each short is either
- // one of the tile_id values from define_tile, or STBTE_EMPTY
- void stbte_set_dimensions(stbte_tilemap *tm, int map_x, int map_y)
- {
- STBTE_ASSERT(map_x >= 0 && map_x <= STBTE_MAX_TILEMAP_X);
- STBTE_ASSERT(map_y >= 0 && map_y <= STBTE_MAX_TILEMAP_Y);
- if (map_x < 0 || map_y < 0 || map_x > STBTE_MAX_TILEMAP_X || map_y > STBTE_MAX_TILEMAP_Y)
- return;
- tm->max_x = map_x;
- tm->max_y = map_y;
- }
- void stbte_clear_map(stbte_tilemap *tm)
- {
- int i,j;
- for (i=0; i < STBTE_MAX_TILEMAP_X * STBTE_MAX_TILEMAP_Y; ++i) {
- tm->data[0][i][0] = tm->background_tile;
- for (j=1; j < tm->num_layers; ++j)
- tm->data[0][i][j] = STBTE__NO_TILE;
- for (j=0; j < STBTE_MAX_PROPERTIES; ++j)
- tm->props[0][i][j] = 0;
- #ifdef STBTE_ALLOW_LINK
- tm->link[0][i].x = -1;
- tm->link[0][i].y = -1;
- tm->linkcount[0][i] = 0;
- #endif
- }
- }
- void stbte_set_tile(stbte_tilemap *tm, int x, int y, int layer, signed short tile)
- {
- STBTE_ASSERT(x >= 0 && x < tm->max_x && y >= 0 && y < tm->max_y);
- STBTE_ASSERT(layer >= 0 && layer < tm->num_layers);
- STBTE_ASSERT(tile >= -1);
- //STBTE_ASSERT(tile < 32768);
- if (x < 0 || x >= STBTE_MAX_TILEMAP_X || y < 0 || y >= STBTE_MAX_TILEMAP_Y)
- return;
- if (layer < 0 || layer >= tm->num_layers || tile < -1)
- return;
- tm->data[y][x][layer] = tile;
- }
- static void stbte__choose_category(stbte_tilemap *tm, int category)
- {
- int i,n=0;
- tm->cur_category = category;
- for (i=0; i < tm->num_tiles; ++i)
- if (tm->tiles[i].category_id == category || category == -1)
- ++n;
- tm->cur_palette_count = n;
- tm->palette_scroll = 0;
- }
- static int stbte__strequal(char *p, char *q)
- {
- while (*p)
- if (*p++ != *q++) return 0;
- return *q == 0;
- }
- static void stbte__compute_tileinfo(stbte_tilemap *tm)
- {
- int i,j;
- tm->num_categories=0;
- for (i=0; i < tm->num_tiles; ++i) {
- stbte__tileinfo *t = &tm->tiles[i];
- // find category
- for (j=0; j < tm->num_categories; ++j)
- if (stbte__strequal(t->category, tm->categories[j]))
- goto found;
- tm->categories[j] = t->category;
- ++tm->num_categories;
- found:
- t->category_id = (unsigned short) j;
- }
- // currently number of categories can never decrease because you
- // can't remove tile definitions, but let's get it right anyway
- if (tm->cur_category > tm->num_categories) {
- tm->cur_category = -1;
- }
- stbte__choose_category(tm, tm->cur_category);
- tm->tileinfo_dirty = 0;
- }
- static void stbte__prepare_tileinfo(stbte_tilemap *tm)
- {
- if (tm->tileinfo_dirty)
- stbte__compute_tileinfo(tm);
- }
- /////////////////////// undo system ////////////////////////
- // the undo system works by storing "commands" into a buffer, and
- // then playing back those commands. undo and redo have to store
- // the commands in different order.
- //
- // the commands are:
- //
- // 1) end_of_undo_record
- // -1:short
- //
- // 2) end_of_redo_record
- // -2:short
- //
- // 3) tile update
- // tile_id:short (-1..32767)
- // x_coord:short
- // y_coord:short
- // layer:short (0..31)
- //
- // 4) property update (also used for links)
- // value_hi:short
- // value_lo:short
- // y_coord:short
- // x_coord:short
- // property:short (256+prop#)
- //
- // Since we use a circular buffer, we might overwrite the undo storage.
- // To detect this, before playing back commands we scan back and see
- // if we see an end_of_undo_record before hitting the relevant boundary,
- // it's wholly contained.
- //
- // When we read back through, we see them in reverse order, so
- // we'll see the layer number or property number first
- //
- // To be clearer about the circular buffer, there are two cases:
- // 1. a single record is larger than the whole buffer.
- // this is caught because the end_of_undo_record will
- // get overwritten.
- // 2. multiple records written are larger than the whole
- // buffer, so some of them have been overwritten by
- // the later ones. this is handled by explicitly tracking
- // the undo length; we never try to parse the data that
- // got overwritten
- // given two points, compute the length between them
- #define stbte__wrap(pos) ((pos) & (STBTE__UNDO_BUFFER_COUNT-1))
- #define STBTE__undo_record -2
- #define STBTE__redo_record -3
- #define STBTE__undo_junk -4 // this is written underneath the undo pointer, never used
- static void stbte__write_undo(stbte_tilemap *tm, short value)
- {
- int pos = tm->undo_pos;
- tm->undo_buffer[pos] = value;
- tm->undo_pos = stbte__wrap(pos+1);
- tm->undo_len += (tm->undo_len < STBTE__UNDO_BUFFER_COUNT-2);
- tm->redo_len -= (tm->redo_len > 0);
- tm->undo_available_valid = 0;
- }
- static void stbte__write_redo(stbte_tilemap *tm, short value)
- {
- int pos = tm->undo_pos;
- tm->undo_buffer[pos] = value;
- tm->undo_pos = stbte__wrap(pos-1);
- tm->redo_len += (tm->redo_len < STBTE__UNDO_BUFFER_COUNT-2);
- tm->undo_len -= (tm->undo_len > 0);
- tm->undo_available_valid = 0;
- }
- static void stbte__begin_undo(stbte_tilemap *tm)
- {
- tm->redo_len = 0;
- stbte__write_undo(tm, STBTE__undo_record);
- stbte__ui.undoing = 1;
- stbte__ui.alert_msg = 0; // clear alert if they start doing something
- }
- static void stbte__end_undo(stbte_tilemap *tm)
- {
- if (stbte__ui.undoing) {
- // check if anything got written
- int pos = stbte__wrap(tm->undo_pos-1);
- if (tm->undo_buffer[pos] == STBTE__undo_record) {
- // empty undo record, move back
- tm->undo_pos = pos;
- STBTE_ASSERT(tm->undo_len > 0);
- tm->undo_len -= 1;
- }
- tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
- // otherwise do nothing
- stbte__ui.undoing = 0;
- }
- }
- static void stbte__undo_record(stbte_tilemap *tm, int x, int y, int i, int v)
- {
- STBTE_ASSERT(stbte__ui.undoing);
- if (stbte__ui.undoing) {
- stbte__write_undo(tm, v);
- stbte__write_undo(tm, x);
- stbte__write_undo(tm, y);
- stbte__write_undo(tm, i);
- }
- }
- static void stbte__redo_record(stbte_tilemap *tm, int x, int y, int i, int v)
- {
- stbte__write_redo(tm, v);
- stbte__write_redo(tm, x);
- stbte__write_redo(tm, y);
- stbte__write_redo(tm, i);
- }
- static float stbte__extract_float(short s0, short s1)
- {
- union { float f; short s[2]; } converter;
- converter.s[0] = s0;
- converter.s[1] = s1;
- return converter.f;
- }
- static short stbte__extract_short(float f, int slot)
- {
- union { float f; short s[2]; } converter;
- converter.f = f;
- return converter.s[slot];
- }
- static void stbte__undo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
- {
- STBTE_ASSERT(stbte__ui.undoing);
- if (stbte__ui.undoing) {
- stbte__write_undo(tm, s1);
- stbte__write_undo(tm, s0);
- stbte__write_undo(tm, x);
- stbte__write_undo(tm, y);
- stbte__write_undo(tm, 256+i);
- }
- }
- static void stbte__undo_record_prop_float(stbte_tilemap *tm, int x, int y, int i, float f)
- {
- stbte__undo_record_prop(tm, x,y,i, stbte__extract_short(f,0), stbte__extract_short(f,1));
- }
- static void stbte__redo_record_prop(stbte_tilemap *tm, int x, int y, int i, short s0, short s1)
- {
- stbte__write_redo(tm, s1);
- stbte__write_redo(tm, s0);
- stbte__write_redo(tm, x);
- stbte__write_redo(tm, y);
- stbte__write_redo(tm, 256+i);
- }
- static int stbte__undo_find_end(stbte_tilemap *tm)
- {
- // first scan through for the end record
- int i, pos = stbte__wrap(tm->undo_pos-1);
- for (i=0; i < tm->undo_len;) {
- STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
- if (tm->undo_buffer[pos] == STBTE__undo_record)
- break;
- if (tm->undo_buffer[pos] >= 255)
- pos = stbte__wrap(pos-5), i += 5;
- else
- pos = stbte__wrap(pos-4), i += 4;
- }
- if (i >= tm->undo_len)
- return -1;
- return pos;
- }
- static void stbte__undo(stbte_tilemap *tm)
- {
- int i, pos, endpos;
- endpos = stbte__undo_find_end(tm);
- if (endpos < 0)
- return;
- // we found a complete undo record
- pos = stbte__wrap(tm->undo_pos-1);
- // start a redo record
- stbte__write_redo(tm, STBTE__redo_record);
- // so now go back through undo and apply in reverse
- // order, and copy it to redo
- for (i=0; endpos != pos; i += 4) {
- int x,y,n,v;
- // get the undo entry
- n = tm->undo_buffer[pos];
- y = tm->undo_buffer[stbte__wrap(pos-1)];
- x = tm->undo_buffer[stbte__wrap(pos-2)];
- v = tm->undo_buffer[stbte__wrap(pos-3)];
- if (n >= 255) {
- short s0=0,s1=0;
- int v2 = tm->undo_buffer[stbte__wrap(pos-4)];
- pos = stbte__wrap(pos-5);
- if (n > 255) {
- float vf = stbte__extract_float(v, v2);
- s0 = stbte__extract_short(tm->props[y][x][n-256], 0);
- s1 = stbte__extract_short(tm->props[y][x][n-256], 1);
- tm->props[y][x][n-256] = vf;
- } else {
- #ifdef STBTE_ALLOW_LINK
- s0 = tm->link[y][x].x;
- s1 = tm->link[y][x].y;
- stbte__set_link(tm, x,y, v, v2, STBTE__undo_none);
- #endif
- }
- // write the redo entry
- stbte__redo_record_prop(tm, x, y, n-256, s0,s1);
- // apply the undo entry
- } else {
- pos = stbte__wrap(pos-4);
- // write the redo entry
- stbte__redo_record(tm, x, y, n, tm->data[y][x][n]);
- // apply the undo entry
- tm->data[y][x][n] = (short) v;
- }
- }
- // overwrite undo record with junk
- tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
- }
- static int stbte__redo_find_end(stbte_tilemap *tm)
- {
- // first scan through for the end record
- int i, pos = stbte__wrap(tm->undo_pos+1);
- for (i=0; i < tm->redo_len;) {
- STBTE_ASSERT(tm->undo_buffer[pos] != STBTE__undo_junk);
- if (tm->undo_buffer[pos] == STBTE__redo_record)
- break;
- if (tm->undo_buffer[pos] >= 255)
- pos = stbte__wrap(pos+5), i += 5;
- else
- pos = stbte__wrap(pos+4), i += 4;
- }
- if (i >= tm->redo_len)
- return -1; // this should only ever happen if redo buffer is empty
- return pos;
- }
- static void stbte__redo(stbte_tilemap *tm)
- {
- // first scan through for the end record
- int i, pos, endpos;
- endpos = stbte__redo_find_end(tm);
- if (endpos < 0)
- return;
- // we found a complete redo record
- pos = stbte__wrap(tm->undo_pos+1);
- // start an undo record
- stbte__write_undo(tm, STBTE__undo_record);
- for (i=0; pos != endpos; i += 4) {
- int x,y,n,v;
- n = tm->undo_buffer[pos];
- y = tm->undo_buffer[stbte__wrap(pos+1)];
- x = tm->undo_buffer[stbte__wrap(pos+2)];
- v = tm->undo_buffer[stbte__wrap(pos+3)];
- if (n >= 255) {
- int v2 = tm->undo_buffer[stbte__wrap(pos+4)];
- short s0=0,s1=0;
- pos = stbte__wrap(pos+5);
- if (n > 255) {
- float vf = stbte__extract_float(v, v2);
- s0 = stbte__extract_short(tm->props[y][x][n-256],0);
- s1 = stbte__extract_short(tm->props[y][x][n-256],1);
- tm->props[y][x][n-256] = vf;
- } else {
- #ifdef STBTE_ALLOW_LINK
- s0 = tm->link[y][x].x;
- s1 = tm->link[y][x].y;
- stbte__set_link(tm, x,y,v,v2, STBTE__undo_none);
- #endif
- }
- // don't use stbte__undo_record_prop because it's guarded
- stbte__write_undo(tm, s1);
- stbte__write_undo(tm, s0);
- stbte__write_undo(tm, x);
- stbte__write_undo(tm, y);
- stbte__write_undo(tm, n);
- } else {
- pos = stbte__wrap(pos+4);
- // don't use stbte__undo_record because it's guarded
- stbte__write_undo(tm, tm->data[y][x][n]);
- stbte__write_undo(tm, x);
- stbte__write_undo(tm, y);
- stbte__write_undo(tm, n);
- tm->data[y][x][n] = (short) v;
- }
- }
- tm->undo_buffer[tm->undo_pos] = STBTE__undo_junk;
- }
- // because detecting that undo is available
- static void stbte__recompute_undo_available(stbte_tilemap *tm)
- {
- tm->undo_available = (stbte__undo_find_end(tm) >= 0);
- tm->redo_available = (stbte__redo_find_end(tm) >= 0);
- }
- static int stbte__undo_available(stbte_tilemap *tm)
- {
- if (!tm->undo_available_valid)
- stbte__recompute_undo_available(tm);
- return tm->undo_available;
- }
- static int stbte__redo_available(stbte_tilemap *tm)
- {
- if (!tm->undo_available_valid)
- stbte__recompute_undo_available(tm);
- return tm->redo_available;
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #ifdef STBTE_ALLOW_LINK
- static void stbte__set_link(stbte_tilemap *tm, int src_x, int src_y, int dest_x, int dest_y, int undo_mode)
- {
- stbte__link *a;
- STBTE_ASSERT(src_x >= 0 && src_x < STBTE_MAX_TILEMAP_X && src_y >= 0 && src_y < STBTE_MAX_TILEMAP_Y);
- a = &tm->link[src_y][src_x];
- // check if it's a do nothing
- if (a->x == dest_x && a->y == dest_y)
- return;
- if (undo_mode != STBTE__undo_none ) {
- if (undo_mode == STBTE__undo_block) stbte__begin_undo(tm);
- stbte__undo_record_prop(tm, src_x, src_y, -1, a->x, a->y);
- if (undo_mode == STBTE__undo_block) stbte__end_undo(tm);
- }
- // check if there's an existing link
- if (a->x >= 0) {
- // decrement existing link refcount
- STBTE_ASSERT(tm->linkcount[a->y][a->x] > 0);
- --tm->linkcount[a->y][a->x];
- }
- // increment new dest
- if (dest_x >= 0) {
- ++tm->linkcount[dest_y][dest_x];
- }
- a->x = dest_x;
- a->y = dest_y;
- }
- #endif
- static void stbte__draw_rect(int x0, int y0, int x1, int y1, unsigned int color)
- {
- STBTE_DRAW_RECT(x0,y0,x1,y1, color);
- }
- #ifdef STBTE_ALLOW_LINK
- static void stbte__draw_line(int x0, int y0, int x1, int y1, unsigned int color)
- {
- int temp;
- if (x1 < x0) temp=x0,x0=x1,x1=temp;
- if (y1 < y0) temp=y0,y0=y1,y1=temp;
- stbte__draw_rect(x0,y0,x1+1,y1+1,color);
- }
- static void stbte__draw_link(int x0, int y0, int x1, int y1, unsigned int color)
- {
- stbte__draw_line(x0,y0,x0,y1, color);
- stbte__draw_line(x0,y1,x1,y1, color);
- }
- #endif
- static void stbte__draw_frame(int x0, int y0, int x1, int y1, unsigned int color)
- {
- stbte__draw_rect(x0,y0,x1-1,y0+1,color);
- stbte__draw_rect(x1-1,y0,x1,y1-1,color);
- stbte__draw_rect(x0+1,y1-1,x1,y1,color);
- stbte__draw_rect(x0,y0+1,x0+1,y1,color);
- }
- static int stbte__get_char_width(int ch)
- {
- return stbte__fontdata[ch-16];
- }
- static short *stbte__get_char_bitmap(int ch)
- {
- return stbte__fontdata + stbte__font_offset[ch-16];
- }
- static void stbte__draw_bitmask_as_columns(int x, int y, short bitmask, int color)
- {
- int start_i = -1, i=0;
- while (bitmask) {
- if (bitmask & (1<<i)) {
- if (start_i < 0)
- start_i = i;
- } else if (start_i >= 0) {
- stbte__draw_rect(x, y+start_i, x+1, y+i, color);
- start_i = -1;
- bitmask &= ~((1<<i)-1); // clear all the old bits; we don't clear them as we go to save code
- }
- ++i;
- }
- }
- static void stbte__draw_bitmap(int x, int y, int w, short *bitmap, int color)
- {
- int i;
- for (i=0; i < w; ++i)
- stbte__draw_bitmask_as_columns(x+i, y, *bitmap++, color);
- }
- static void stbte__draw_text_core(int x, int y, const char *str, int w, int color, int digitspace)
- {
- int x_end = x+w;
- while (*str) {
- int c = *str++;
- int cw = stbte__get_char_width(c);
- if (x + cw > x_end)
- break;
- stbte__draw_bitmap(x, y, cw, stbte__get_char_bitmap(c), color);
- if (digitspace && c == ' ')
- cw = stbte__get_char_width('0');
- x += cw+1;
- }
- }
- static void stbte__draw_text(int x, int y, const char *str, int w, int color)
- {
- stbte__draw_text_core(x,y,str,w,color,0);
- }
- static int stbte__text_width(const char *str)
- {
- int x = 0;
- while (*str) {
- int c = *str++;
- int cw = stbte__get_char_width(c);
- x += cw+1;
- }
- return x;
- }
- static void stbte__draw_frame_delayed(int x0, int y0, int x1, int y1, int color)
- {
- if (stbte__ui.delaycount < STBTE__MAX_DELAYRECT) {
- stbte__colorrect r = { x0,y0,x1,y1,color };
- stbte__ui.delayrect[stbte__ui.delaycount++] = r;
- }
- }
- static void stbte__flush_delay(void)
- {
- stbte__colorrect *r;
- int i;
- r = stbte__ui.delayrect;
- for (i=0; i < stbte__ui.delaycount; ++i,++r)
- stbte__draw_frame(r->x0,r->y0,r->x1,r->y1,r->color);
- stbte__ui.delaycount = 0;
- }
- static void stbte__activate(int id)
- {
- stbte__ui.active_id = id;
- stbte__ui.active_event = stbte__ui.event;
- stbte__ui.accum_x = 0;
- stbte__ui.accum_y = 0;
- }
- static int stbte__hittest(int x0, int y0, int x1, int y1, int id)
- {
- int over = stbte__ui.mx >= x0 && stbte__ui.my >= y0
- && stbte__ui.mx < x1 && stbte__ui.my < y1;
- if (over && stbte__ui.event >= STBTE__tick)
- stbte__ui.next_hot_id = id;
- return over;
- }
- static int stbte__button_core(int id)
- {
- switch (stbte__ui.event) {
- case STBTE__leftdown:
- if (stbte__ui.hot_id == id && STBTE__INACTIVE())
- stbte__activate(id);
- break;
- case STBTE__leftup:
- if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) {
- stbte__activate(0);
- return 1;
- }
- break;
- case STBTE__rightdown:
- if (stbte__ui.hot_id == id && STBTE__INACTIVE())
- stbte__activate(id);
- break;
- case STBTE__rightup:
- if (stbte__ui.active_id == id && STBTE__IS_HOT(id)) {
- stbte__activate(0);
- return -1;
- }
- break;
- }
- return 0;
- }
- static void stbte__draw_box(int x0, int y0, int x1, int y1, int colormode, int colorindex)
- {
- stbte__draw_rect (x0,y0,x1,y1, stbte__color_table[colormode][STBTE__base ][colorindex]);
- stbte__draw_frame(x0,y0,x1,y1, stbte__color_table[colormode][STBTE__outline][colorindex]);
- }
- static void stbte__draw_textbox(int x0, int y0, int x1, int y1, char *text, int xoff, int yoff, int colormode, int colorindex)
- {
- stbte__draw_box(x0,y0,x1,y1,colormode,colorindex);
- stbte__draw_text(x0+xoff,y0+yoff, text, x1-x0-xoff-1, stbte__color_table[colormode][STBTE__text][colorindex]);
- }
- static int stbte__button(int colormode, const char *label, int x, int y, int textoff, int width, int id, int toggled, int disabled)
- {
- int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
- int s = STBTE__BUTTON_INTERNAL_SPACING;
- if(!disabled) stbte__hittest(x0,y0,x1,y1,id);
- if (stbte__ui.event == STBTE__paint)
- stbte__draw_textbox(x0,y0,x1,y1, (char*) label,s+textoff,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
- if (disabled)
- return 0;
- return (stbte__button_core(id) == 1);
- }
- static int stbte__button_icon(int colormode, char ch, int x, int y, int width, int id, int toggled, int disabled)
- {
- int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
- int s = STBTE__BUTTON_INTERNAL_SPACING;
- stbte__hittest(x0,y0,x1,y1,id);
- if (stbte__ui.event == STBTE__paint) {
- char label[2] = { ch, 0 };
- int pad = (9 - stbte__get_char_width(ch))/2;
- stbte__draw_textbox(x0,y0,x1,y1, label,s+pad,s, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
- }
- if (disabled)
- return 0;
- return (stbte__button_core(id) == 1);
- }
- static int stbte__minibutton(int colormode, int x, int y, int ch, int id)
- {
- int x0 = x, y0 = y, x1 = x+8, y1 = y+7;
- stbte__hittest(x0,y0,x1,y1,id);
- if (stbte__ui.event == STBTE__paint) {
- char str[2] = { (char)ch, 0 };
- stbte__draw_textbox(x0,y0,x1,y1, str,1,0,colormode, STBTE__INDEX_FOR_ID(id,0,0));
- }
- return stbte__button_core(id);
- }
- static int stbte__layerbutton(int x, int y, int ch, int id, int toggled, int disabled, int colormode)
- {
- int x0 = x, y0 = y, x1 = x+10, y1 = y+11;
- if(!disabled) stbte__hittest(x0,y0,x1,y1,id);
- if (stbte__ui.event == STBTE__paint) {
- char str[2] = { (char)ch, 0 };
- int off = (9-stbte__get_char_width(ch))/2;
- stbte__draw_textbox(x0,y0,x1,y1, str, off+1,2, colormode, STBTE__INDEX_FOR_ID(id,disabled,toggled));
- }
- if (disabled)
- return 0;
- return stbte__button_core(id);
- }
- static int stbte__microbutton(int x, int y, int size, int id, int colormode)
- {
- int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
- stbte__hittest(x0,y0,x1,y1,id);
- if (stbte__ui.event == STBTE__paint) {
- stbte__draw_box(x0,y0,x1,y1, colormode, STBTE__INDEX_FOR_ID(id,0,0));
- }
- return stbte__button_core(id);
- }
- static int stbte__microbutton_dragger(int x, int y, int size, int id, int *pos)
- {
- int x0 = x, y0 = y, x1 = x+size, y1 = y+size;
- stbte__hittest(x0,y0,x1,y1,id);
- switch (stbte__ui.event) {
- case STBTE__paint:
- stbte__draw_box(x0,y0,x1,y1, STBTE__cexpander, STBTE__INDEX_FOR_ID(id,0,0));
- break;
- case STBTE__leftdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
- stbte__activate(id);
- stbte__ui.sx = stbte__ui.mx - *pos;
- }
- break;
- case STBTE__mousemove:
- if (STBTE__IS_ACTIVE(id) && stbte__ui.active_event == STBTE__leftdown) {
- *pos = stbte__ui.mx - stbte__ui.sx;
- }
- break;
- case STBTE__leftup:
- if (STBTE__IS_ACTIVE(id))
- stbte__activate(0);
- break;
- default:
- return stbte__button_core(id);
- }
- return 0;
- }
- static int stbte__category_button(const char *label, int x, int y, int width, int id, int toggled)
- {
- int x0=x,y0=y, x1=x+width,y1=y+STBTE__BUTTON_HEIGHT;
- int s = STBTE__BUTTON_INTERNAL_SPACING;
- stbte__hittest(x0,y0,x1,y1,id);
- if (stbte__ui.event == STBTE__paint)
- stbte__draw_textbox(x0,y0,x1,y1, (char*) label, s,s, STBTE__ccategory_button, STBTE__INDEX_FOR_ID(id,0,toggled));
- return (stbte__button_core(id) == 1);
- }
- enum
- {
- STBTE__none,
- STBTE__begin,
- STBTE__end,
- STBTE__change,
- };
- // returns -1 if value changes, 1 at end of drag
- static int stbte__slider(int x0, int w, int y, int range, int *value, int id)
- {
- int x1 = x0+w;
- int pos = *value * w / (range+1);
- stbte__hittest(x0,y-2,x1,y+3,id);
- int event_mouse_move = STBTE__change;
- switch (stbte__ui.event) {
- case STBTE__paint:
- stbte__draw_rect(x0,y,x1,y+1, 0x808080);
- stbte__draw_rect(x0+pos-1,y-1,x0+pos+2,y+2, 0xffffff);
- break;
- case STBTE__leftdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
- stbte__activate(id);
- event_mouse_move = STBTE__begin;
- }
- // fall through
- case STBTE__mousemove:
- if (STBTE__IS_ACTIVE(id)) {
- int v = (stbte__ui.mx-x0)*(range+1)/w;
- if (v < 0) v = 0; else if (v > range) v = range;
- *value = v;
- return event_mouse_move;
- }
- break;
- case STBTE__leftup:
- if (STBTE__IS_ACTIVE(id)) {
- stbte__activate(0);
- return STBTE__end;
- }
- break;
- }
- return STBTE__none;
- }
- #if defined(_WIN32) && defined(__STDC_WANT_SECURE_LIB__)
- #define stbte__sprintf sprintf_s
- #define stbte__sizeof(s) , sizeof(s)
- #else
- #define stbte__sprintf sprintf
- #define stbte__sizeof(s)
- #endif
- static int stbte__float_control(int x0, int y0, int w, float minv, float maxv, float scale, const char *fmt, float *value, int colormode, int id)
- {
- int x1 = x0+w;
- int y1 = y0+11;
- stbte__hittest(x0,y0,x1,y1,id);
- switch (stbte__ui.event) {
- case STBTE__paint: {
- char text[32];
- stbte__sprintf(text stbte__sizeof(text), fmt ? fmt : "%6.2f", *value);
- stbte__draw_textbox(x0,y0,x1,y1, text, 1,2, colormode, STBTE__INDEX_FOR_ID(id,0,0));
- break;
- }
- case STBTE__leftdown:
- case STBTE__rightdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE())
- stbte__activate(id);
- return STBTE__begin;
- break;
- case STBTE__leftup:
- case STBTE__rightup:
- if (STBTE__IS_ACTIVE(id)) {
- stbte__activate(0);
- return STBTE__end;
- }
- break;
- case STBTE__mousemove:
- if (STBTE__IS_ACTIVE(id)) {
- float v = *value, delta;
- int ax = stbte__ui.accum_x/STBTE_FLOAT_CONTROL_GRANULARITY;
- int ay = stbte__ui.accum_y/STBTE_FLOAT_CONTROL_GRANULARITY;
- stbte__ui.accum_x -= ax*STBTE_FLOAT_CONTROL_GRANULARITY;
- stbte__ui.accum_y -= ay*STBTE_FLOAT_CONTROL_GRANULARITY;
- if (stbte__ui.shift) {
- if (stbte__ui.active_event == STBTE__leftdown)
- delta = ax * 16.0f + ay;
- else
- delta = ax / 16.0f + ay / 256.0f;
- } else {
- if (stbte__ui.active_event == STBTE__leftdown)
- delta = ax*10.0f + ay;
- else
- delta = ax * 0.1f + ay * 0.01f;
- }
- v += delta * scale;
- if (v < minv) v = minv;
- if (v > maxv) v = maxv;
- *value = v;
- return STBTE__change;
- }
- break;
- }
- return STBTE__none;
- }
- static void stbte__scrollbar(int x, int y0, int y1, int *val, int v0, int v1, int num_vis, int id)
- {
- int thumbpos;
- if (v1 - v0 <= num_vis)
- return;
- // generate thumbpos from numvis
- thumbpos = y0+2 + (y1-y0-4) * *val / (v1 - v0 - num_vis);
- if (thumbpos < y0) thumbpos = y0;
- if (thumbpos >= y1) thumbpos = y1;
- stbte__hittest(x-1,y0,x+2,y1,id);
- switch (stbte__ui.event) {
- case STBTE__paint:
- stbte__draw_rect(x,y0,x+1,y1, stbte__color_table[STBTE__cscrollbar][STBTE__text][STBTE__idle]);
- stbte__draw_box(x-1,thumbpos-3,x+2,thumbpos+4, STBTE__cscrollbar, STBTE__INDEX_FOR_ID(id,0,0));
- break;
- case STBTE__leftdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
- // check if it's over the thumb
- stbte__activate(id);
- *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0);
- }
- break;
- case STBTE__mousemove:
- if (STBTE__IS_ACTIVE(id) && stbte__ui.mx >= x-15 && stbte__ui.mx <= x+15)
- *val = ((stbte__ui.my-y0) * (v1 - v0 - num_vis) + (y1-y0)/2)/ (y1-y0);
- break;
- case STBTE__leftup:
- if (STBTE__IS_ACTIVE(id))
- stbte__activate(0);
- break;
- }
- if (*val >= v1-num_vis)
- *val = v1-num_vis;
- if (*val <= v0)
- *val = v0;
- }
- static void stbte__compute_digits(stbte_tilemap *tm)
- {
- if (tm->max_x >= 1000 || tm->max_y >= 1000)
- tm->digits = 4;
- else if (tm->max_x >= 100 || tm->max_y >= 100)
- tm->digits = 3;
- else
- tm->digits = 2;
- }
- static int stbte__is_single_selection(void)
- {
- return stbte__ui.has_selection
- && stbte__ui.select_x0 == stbte__ui.select_x1
- && stbte__ui.select_y0 == stbte__ui.select_y1;
- }
- typedef struct
- {
- int width, height;
- int x,y;
- int active;
- float retracted;
- } stbte__region_t;
- static stbte__region_t stbte__region[4];
- #define STBTE__TOOLBAR_ICON_SIZE (9+2*2)
- #define STBTE__TOOLBAR_PASTE_SIZE (34+2*2)
- // This routine computes where every panel goes onscreen: computes
- // a minimum width for each side based on which panels are on that
- // side, and accounts for width-dependent layout of certain panels.
- static void stbte__compute_panel_locations(stbte_tilemap *tm)
- {
- int i, limit, w, k;
- int window_width = stbte__ui.x1 - stbte__ui.x0;
- int window_height = stbte__ui.y1 - stbte__ui.y0;
- int min_width[STBTE__num_panel]={0,0,0,0,0,0,0};
- int height[STBTE__num_panel]={0,0,0,0,0,0,0};
- int panel_active[STBTE__num_panel]={1,0,1,1,1,1,1};
- int vpos[4] = { 0,0,0,0 };
- stbte__panel *p = stbte__ui.panel;
- stbte__panel *pt = &p[STBTE__panel_toolbar];
- #ifdef STBTE__NO_PROPS
- int props = 0;
- #else
- int props = 1;
- #endif
- for (i=0; i < 4; ++i) {
- stbte__region[i].active = 0;
- stbte__region[i].width = 0;
- stbte__region[i].height = 0;
- }
- // compute number of digits needs for info panel
- stbte__compute_digits(tm);
- // determine which panels are active
- panel_active[STBTE__panel_categories] = tm->num_categories != 0;
- panel_active[STBTE__panel_layers ] = tm->num_layers > 1;
- #ifdef STBTE__COLORPICKER
- panel_active[STBTE__panel_colorpick ] = 1;
- #endif
- panel_active[STBTE__panel_props ] = props && stbte__is_single_selection();
- // compute minimum widths for each panel (assuming they're on sides not top)
- min_width[STBTE__panel_info ] = 8 + 11 + 7*tm->digits+17+7; // estimate min width of "w:0000"
- min_width[STBTE__panel_colorpick ] = 120;
- min_width[STBTE__panel_tiles ] = 4 + tm->palette_spacing_x + 5; // 5 for scrollbar
- min_width[STBTE__panel_categories] = 4 + 42 + 5; // 42 is enough to show ~7 chars; 5 for scrollbar
- min_width[STBTE__panel_layers ] = 4 + 54 + 30*tm->has_layer_names; // 2 digits plus 3 buttons plus scrollbar
- min_width[STBTE__panel_toolbar ] = 4 + STBTE__TOOLBAR_PASTE_SIZE; // wide enough for 'Paste' button
- min_width[STBTE__panel_props ] = 80; // narrowest info panel
- // compute minimum widths for left & right panels based on the above
- stbte__region[0].width = stbte__ui.left_width;
- stbte__region[1].width = stbte__ui.right_width;
- for (i=0; i < STBTE__num_panel; ++i) {
- if (panel_active[i]) {
- int side = stbte__ui.panel[i].side;
- if (min_width[i] > stbte__region[side].width)
- stbte__region[side].width = min_width[i];
- stbte__region[side].active = 1;
- }
- }
- // now compute the heights of each panel
- // if toolbar at top, compute its size & push the left and right start points down
- if (stbte__region[STBTE__side_top].active) {
- int height = STBTE__TOOLBAR_ICON_SIZE+2;
- pt->x0 = stbte__ui.x0;
- pt->y0 = stbte__ui.y0;
- pt->width = window_width;
- pt->height = height;
- vpos[STBTE__side_left] = vpos[STBTE__side_right] = height;
- } else {
- int num_rows = STBTE__num_tool * ((stbte__region[pt->side].width-4)/STBTE__TOOLBAR_ICON_SIZE);
- height[STBTE__panel_toolbar] = num_rows*13 + 3*15 + 4; // 3*15 for cut/copy/paste, which are stacked vertically
- }
- for (i=0; i < 4; ++i)
- stbte__region[i].y = stbte__ui.y0 + vpos[i];
- for (i=0; i < 2; ++i) {
- int anim = (int) (stbte__region[i].width * stbte__region[i].retracted);
- stbte__region[i].x = (i == STBTE__side_left) ? stbte__ui.x0 - anim : stbte__ui.x1 - stbte__region[i].width + anim;
- }
- // color picker
- height[STBTE__panel_colorpick] = 300;
- // info panel
- w = stbte__region[p[STBTE__panel_info].side].width;
- p[STBTE__panel_info].mode = (w >= 8 + (11+7*tm->digits+17)*2 + 4);
- if (p[STBTE__panel_info].mode)
- height[STBTE__panel_info] = 5 + 11*2 + 2 + tm->palette_spacing_y;
- else
- height[STBTE__panel_info] = 5 + 11*4 + 2 + tm->palette_spacing_y;
- // layers
- limit = 6 + stbte__ui.panel[STBTE__panel_layers].delta_height;
- height[STBTE__panel_layers] = (tm->num_layers > limit ? limit : tm->num_layers)*15 + 7 + (tm->has_layer_names ? 0 : 11) + props*13;
- // categories
- limit = 6 + stbte__ui.panel[STBTE__panel_categories].delta_height;
- height[STBTE__panel_categories] = (tm->num_categories+1 > limit ? limit : tm->num_categories+1)*11 + 14;
- if (stbte__ui.panel[STBTE__panel_categories].side == stbte__ui.panel[STBTE__panel_categories].side)
- height[STBTE__panel_categories] -= 4;
- // palette
- k = (stbte__region[p[STBTE__panel_tiles].side].width - 8) / tm->palette_spacing_x;
- if (k == 0) k = 1;
- height[STBTE__panel_tiles] = ((tm->num_tiles+k-1)/k) * tm->palette_spacing_y + 8;
- // properties panel
- height[STBTE__panel_props] = 9 + STBTE_MAX_PROPERTIES*14;
- // now compute the locations of all the panels
- for (i=0; i < STBTE__num_panel; ++i) {
- if (panel_active[i]) {
- int side = p[i].side;
- if (side == STBTE__side_left || side == STBTE__side_right) {
- p[i].width = stbte__region[side].width;
- p[i].x0 = stbte__region[side].x;
- p[i].y0 = stbte__ui.y0 + vpos[side];
- p[i].height = height[i];
- vpos[side] += height[i];
- if (vpos[side] > window_height) {
- vpos[side] = window_height;
- p[i].height = stbte__ui.y1 - p[i].y0;
- }
- } else {
- ; // it's at top, it's already been explicitly set up earlier
- }
- } else {
- // inactive panel
- p[i].height = 0;
- p[i].width = 0;
- p[i].x0 = stbte__ui.x1;
- p[i].y0 = stbte__ui.y1;
- }
- }
- }
- // unique identifiers for imgui
- enum
- {
- STBTE__map=1,
- STBTE__region,
- STBTE__panel, // panel background to hide map, and misc controls
- STBTE__info, // info data
- STBTE__toolbarA, STBTE__toolbarB, // toolbar buttons: param is tool number
- STBTE__palette, // palette selectors: param is tile index
- STBTE__categories, // category selectors: param is category index
- STBTE__layer, //
- STBTE__solo, STBTE__hide, STBTE__lock, // layer controls: param is layer
- STBTE__scrollbar, // param is panel ID
- STBTE__panel_mover, // p1 is panel ID, p2 is destination side
- STBTE__panel_sizer, // param panel ID
- STBTE__scrollbar_id,
- STBTE__colorpick_id,
- STBTE__prop_flag,
- STBTE__prop_float,
- STBTE__prop_int,
- };
- // id is: [ 24-bit data : 7-bit identifier ]
- // map id is: [ 12-bit y : 12 bit x : 7-bit identifier ]
- #define STBTE__ID(n,p) ((n) + ((p)<<7))
- #define STBTE__ID2(n,p,q) STBTE__ID(n, ((p)<<12)+(q) )
- #define STBTE__IDMAP(x,y) STBTE__ID2(STBTE__map, x,y)
- static void stbte__activate_map(int x, int y)
- {
- stbte__ui.active_id = STBTE__IDMAP(x,y);
- stbte__ui.active_event = stbte__ui.event;
- stbte__ui.sx = x;
- stbte__ui.sy = y;
- }
- static void stbte__alert(const char *msg)
- {
- stbte__ui.alert_msg = msg;
- stbte__ui.alert_timer = 3;
- }
- #define STBTE__BG(tm,layer) ((layer) == 0 ? (tm)->background_tile : STBTE__NO_TILE)
- static void stbte__brush_predict(stbte_tilemap *tm, short result[])
- {
- stbte__tileinfo *ti;
- int i;
- if (tm->cur_tile < 0) return;
- ti = &tm->tiles[tm->cur_tile];
- // find lowest legit layer to paint it on, and put it there
- for (i=0; i < tm->num_layers; ++i) {
- // check if object is allowed on layer
- if (!(ti->layermask & (1 << i)))
- continue;
- if (i != tm->solo_layer) {
- // if there's a selected layer, can only paint on that
- if (tm->cur_layer >= 0 && i != tm->cur_layer)
- continue;
- // if the layer is hidden, we can't see it
- if (tm->layerinfo[i].hidden)
- continue;
- // if the layer is locked, we can't write to it
- if (tm->layerinfo[i].locked == STBTE__locked)
- continue;
- // if the layer is non-empty and protected, can't write to it
- if (tm->layerinfo[i].locked == STBTE__protected && result[i] != STBTE__BG(tm,i))
- continue;
- }
- result[i] = ti->id;
- return;
- }
- }
- static void stbte__brush(stbte_tilemap *tm, int x, int y)
- {
- stbte__tileinfo *ti;
- // find lowest legit layer to paint it on, and put it there
- int i;
- if (tm->cur_tile < 0) return;
- ti = &tm->tiles[tm->cur_tile];
- for (i=0; i < tm->num_layers; ++i) {
- // check if object is allowed on layer
- if (!(ti->layermask & (1 << i)))
- continue;
- if (i != tm->solo_layer) {
- // if there's a selected layer, can only paint on that
- if (tm->cur_layer >= 0 && i != tm->cur_layer)
- continue;
- // if the layer is hidden, we can't see it
- if (tm->layerinfo[i].hidden)
- continue;
- // if the layer is locked, we can't write to it
- if (tm->layerinfo[i].locked == STBTE__locked)
- continue;
- // if the layer is non-empty and protected, can't write to it
- if (tm->layerinfo[i].locked == STBTE__protected && tm->data[y][x][i] != STBTE__BG(tm,i))
- continue;
- }
- stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
- tm->data[y][x][i] = ti->id;
- return;
- }
- //stbte__alert("Selected tile not valid on active layer(s)");
- }
- enum
- {
- STBTE__erase_none = -1,
- STBTE__erase_brushonly = 0,
- STBTE__erase_any = 1,
- STBTE__erase_all = 2,
- };
- static int stbte__erase_predict(stbte_tilemap *tm, short result[], int allow_any)
- {
- stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL;
- int i;
- if (allow_any == STBTE__erase_none)
- return allow_any;
- // first check if only one layer is legit
- i = tm->cur_layer;
- if (tm->solo_layer >= 0)
- i = tm->solo_layer;
- // if only one layer is legit, directly process that one for clarity
- if (i >= 0) {
- short bg = (i == 0 ? tm->background_tile : -1);
- if (tm->solo_layer < 0) {
- // check that we're allowed to write to it
- if (tm->layerinfo[i].hidden) return STBTE__erase_none;
- if (tm->layerinfo[i].locked) return STBTE__erase_none;
- }
- if (result[i] == bg)
- return STBTE__erase_none; // didn't erase anything
- if (ti && result[i] == ti->id && (i != 0 || ti->id != tm->background_tile)) {
- result[i] = bg;
- return STBTE__erase_brushonly;
- }
- if (allow_any == STBTE__erase_any) {
- result[i] = bg;
- return STBTE__erase_any;
- }
- return STBTE__erase_none;
- }
- // if multiple layers are legit, first scan all for brush data
- if (ti && allow_any != STBTE__erase_all) {
- for (i=tm->num_layers-1; i >= 0; --i) {
- if (result[i] != ti->id)
- continue;
- if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
- continue;
- if (i == 0 && result[i] == tm->background_tile)
- return STBTE__erase_none;
- result[i] = STBTE__BG(tm,i);
- return STBTE__erase_brushonly;
- }
- }
- if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all)
- return STBTE__erase_none;
- // apply layer filters, erase from top
- for (i=tm->num_layers-1; i >= 0; --i) {
- if (result[i] < 0)
- continue;
- if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
- continue;
- if (i == 0 && result[i] == tm->background_tile)
- return STBTE__erase_none;
- result[i] = STBTE__BG(tm,i);
- if (allow_any != STBTE__erase_all)
- return STBTE__erase_any;
- }
- if (allow_any == STBTE__erase_all)
- return allow_any;
- return STBTE__erase_none;
- }
- static int stbte__erase(stbte_tilemap *tm, int x, int y, int allow_any)
- {
- stbte__tileinfo *ti = tm->cur_tile >= 0 ? &tm->tiles[tm->cur_tile] : NULL;
- int i;
- if (allow_any == STBTE__erase_none)
- return allow_any;
- // first check if only one layer is legit
- i = tm->cur_layer;
- if (tm->solo_layer >= 0)
- i = tm->solo_layer;
- // if only one layer is legit, directly process that one for clarity
- if (i >= 0) {
- short bg = (i == 0 ? tm->background_tile : -1);
- if (tm->solo_layer < 0) {
- // check that we're allowed to write to it
- if (tm->layerinfo[i].hidden) return STBTE__erase_none;
- if (tm->layerinfo[i].locked) return STBTE__erase_none;
- }
- if (tm->data[y][x][i] == bg)
- return -1; // didn't erase anything
- if (ti && tm->data[y][x][i] == ti->id && (i != 0 || ti->id != tm->background_tile)) {
- stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
- tm->data[y][x][i] = bg;
- return STBTE__erase_brushonly;
- }
- if (allow_any == STBTE__erase_any) {
- stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
- tm->data[y][x][i] = bg;
- return STBTE__erase_any;
- }
- return STBTE__erase_none;
- }
- // if multiple layers are legit, first scan all for brush data
- if (ti && allow_any != STBTE__erase_all) {
- for (i=tm->num_layers-1; i >= 0; --i) {
- if (tm->data[y][x][i] != ti->id)
- continue;
- if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
- continue;
- if (i == 0 && tm->data[y][x][i] == tm->background_tile)
- return STBTE__erase_none;
- stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
- tm->data[y][x][i] = STBTE__BG(tm,i);
- return STBTE__erase_brushonly;
- }
- }
- if (allow_any != STBTE__erase_any && allow_any != STBTE__erase_all)
- return STBTE__erase_none;
- // apply layer filters, erase from top
- for (i=tm->num_layers-1; i >= 0; --i) {
- if (tm->data[y][x][i] < 0)
- continue;
- if (tm->layerinfo[i].locked || tm->layerinfo[i].hidden)
- continue;
- if (i == 0 && tm->data[y][x][i] == tm->background_tile)
- return STBTE__erase_none;
- stbte__undo_record(tm,x,y,i,tm->data[y][x][i]);
- tm->data[y][x][i] = STBTE__BG(tm,i);
- if (allow_any != STBTE__erase_all)
- return STBTE__erase_any;
- }
- if (allow_any == STBTE__erase_all)
- return allow_any;
- return STBTE__erase_none;
- }
- static int stbte__find_tile(stbte_tilemap *tm, int tile_id)
- {
- int i;
- for (i=0; i < tm->num_tiles; ++i)
- if (tm->tiles[i].id == tile_id)
- return i;
- stbte__alert("Eyedropped tile that isn't in tileset");
- return -1;
- }
- static void stbte__eyedrop(stbte_tilemap *tm, int x, int y)
- {
- int i,j;
- // flush eyedropper state
- if (stbte__ui.eyedrop_x != x || stbte__ui.eyedrop_y != y) {
- stbte__ui.eyedrop_x = x;
- stbte__ui.eyedrop_y = y;
- stbte__ui.eyedrop_last_layer = tm->num_layers;
- }
- // if only one layer is active, query that
- i = tm->cur_layer;
- if (tm->solo_layer >= 0)
- i = tm->solo_layer;
- if (i >= 0) {
- if (tm->data[y][x][i] == STBTE__NO_TILE)
- return;
- tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]);
- return;
- }
- // if multiple layers, continue from previous
- i = stbte__ui.eyedrop_last_layer;
- for (j=0; j < tm->num_layers; ++j) {
- if (--i < 0)
- i = tm->num_layers-1;
- if (tm->layerinfo[i].hidden)
- continue;
- if (tm->data[y][x][i] == STBTE__NO_TILE)
- continue;
- stbte__ui.eyedrop_last_layer = i;
- tm->cur_tile = stbte__find_tile(tm, tm->data[y][x][i]);
- return;
- }
- }
- static int stbte__should_copy_properties(stbte_tilemap *tm)
- {
- int i;
- if (tm->propmode == STBTE__propmode_always)
- return 1;
- if (tm->propmode == STBTE__propmode_never)
- return 0;
- if (tm->solo_layer >= 0 || tm->cur_layer >= 0)
- return 0;
- for (i=0; i < tm->num_layers; ++i)
- if (tm->layerinfo[i].hidden || tm->layerinfo[i].locked)
- return 0;
- return 1;
- }
- // compute the result of pasting into a tile non-destructively so we can preview it
- static void stbte__paste_stack(stbte_tilemap *tm, short result[], short dest[], short src[], int dragging)
- {
- int i;
- // special case single-layer
- i = tm->cur_layer;
- if (tm->solo_layer >= 0)
- i = tm->solo_layer;
- if (i >= 0) {
- if (tm->solo_layer < 0) {
- // check that we're allowed to write to it
- if (tm->layerinfo[i].hidden) return;
- if (tm->layerinfo[i].locked == STBTE__locked) return;
- // if protected, dest has to be empty
- if (tm->layerinfo[i].locked == STBTE__protected && dest[i] != STBTE__BG(tm,i)) return;
- // if dragging w/o copy, we will try to erase stuff, which protection disallows
- if (dragging && tm->layerinfo[i].locked == STBTE__protected)
- return;
- }
- result[i] = dest[i];
- if (src[i] != STBTE__BG(tm,i))
- result[i] = src[i];
- return;
- }
- for (i=0; i < tm->num_layers; ++i) {
- result[i] = dest[i];
- if (src[i] != STBTE__NO_TILE)
- if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked != STBTE__locked)
- if (tm->layerinfo[i].locked == STBTE__unlocked || (!dragging && dest[i] == STBTE__BG(tm,i)))
- result[i] = src[i];
- }
- }
- // compute the result of dragging away from a tile
- static void stbte__clear_stack(stbte_tilemap *tm, short result[])
- {
- int i;
- // special case single-layer
- i = tm->cur_layer;
- if (tm->solo_layer >= 0)
- i = tm->solo_layer;
- if (i >= 0)
- result[i] = STBTE__BG(tm,i);
- else
- for (i=0; i < tm->num_layers; ++i)
- if (!tm->layerinfo[i].hidden && tm->layerinfo[i].locked == STBTE__unlocked)
- result[i] = STBTE__BG(tm,i);
- }
- // check if some map square is active
- #define STBTE__IS_MAP_ACTIVE() ((stbte__ui.active_id & 127) == STBTE__map)
- #define STBTE__IS_MAP_HOT() ((stbte__ui.hot_id & 127) == STBTE__map)
- static void stbte__fillrect(stbte_tilemap *tm, int x0, int y0, int x1, int y1, int fill)
- {
- int i,j;
- stbte__begin_undo(tm);
- if (x0 > x1) i=x0,x0=x1,x1=i;
- if (y0 > y1) j=y0,y0=y1,y1=j;
- for (j=y0; j <= y1; ++j)
- for (i=x0; i <= x1; ++i)
- if (fill)
- stbte__brush(tm, i,j);
- else
- stbte__erase(tm, i,j,STBTE__erase_any);
- stbte__end_undo(tm);
- // suppress warning from brush
- stbte__ui.alert_msg = 0;
- }
- static void stbte__select_rect(stbte_tilemap *tm, int x0, int y0, int x1, int y1)
- {
- stbte__ui.has_selection = 1;
- stbte__ui.select_x0 = (x0 < x1 ? x0 : x1);
- stbte__ui.select_x1 = (x0 < x1 ? x1 : x0);
- stbte__ui.select_y0 = (y0 < y1 ? y0 : y1);
- stbte__ui.select_y1 = (y0 < y1 ? y1 : y0);
- }
- static void stbte__copy_properties(float *dest, float *src)
- {
- int i;
- for (i=0; i < STBTE_MAX_PROPERTIES; ++i)
- dest[i] = src[i];
- }
- static void stbte__copy_cut(stbte_tilemap *tm, int cut)
- {
- int i,j,n,w,h,p=0;
- int copy_props = stbte__should_copy_properties(tm);
- if (!stbte__ui.has_selection)
- return;
- w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1;
- h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1;
- if (STBTE_MAX_COPY / w < h) {
- stbte__alert("Selection too large for copy buffer, increase STBTE_MAX_COPY");
- return;
- }
- for (i=0; i < w*h; ++i)
- for (n=0; n < tm->num_layers; ++n)
- stbte__ui.copybuffer[i][n] = STBTE__NO_TILE;
- if (cut)
- stbte__begin_undo(tm);
- for (j=stbte__ui.select_y0; j <= stbte__ui.select_y1; ++j) {
- for (i=stbte__ui.select_x0; i <= stbte__ui.select_x1; ++i) {
- for (n=0; n < tm->num_layers; ++n) {
- if (tm->solo_layer >= 0) {
- if (tm->solo_layer != n)
- continue;
- } else {
- if (tm->cur_layer >= 0)
- if (tm->cur_layer != n)
- continue;
- if (tm->layerinfo[n].hidden)
- continue;
- if (cut && tm->layerinfo[n].locked)
- continue;
- }
- stbte__ui.copybuffer[p][n] = tm->data[j][i][n];
- if (cut) {
- stbte__undo_record(tm,i,j,n, tm->data[j][i][n]);
- tm->data[j][i][n] = (n==0 ? tm->background_tile : -1);
- }
- }
- if (copy_props) {
- stbte__copy_properties(stbte__ui.copyprops[p], tm->props[j][i]);
- #ifdef STBTE_ALLOW_LINK
- stbte__ui.copylinks[p] = tm->link[j][i];
- if (cut)
- stbte__set_link(tm, i,j,-1,-1, STBTE__undo_record);
- #endif
- }
- ++p;
- }
- }
- if (cut)
- stbte__end_undo(tm);
- stbte__ui.copy_width = w;
- stbte__ui.copy_height = h;
- stbte__ui.has_copy = 1;
- //stbte__ui.has_selection = 0;
- stbte__ui.copy_has_props = copy_props;
- stbte__ui.copy_src = tm; // used to give better semantics when copying links
- stbte__ui.copy_src_x = stbte__ui.select_x0;
- stbte__ui.copy_src_y = stbte__ui.select_y0;
- }
- static int stbte__in_rect(int x, int y, int x0, int y0, int w, int h)
- {
- return x >= x0 && x < x0+w && y >= y0 && y < y0+h;
- }
- #ifdef STBTE_ALLOW_LINK
- static int stbte__in_src_rect(int x, int y)
- {
- return stbte__in_rect(x,y, stbte__ui.copy_src_x, stbte__ui.copy_src_y, stbte__ui.copy_width, stbte__ui.copy_height);
- }
- static int stbte__in_dest_rect(int x, int y, int destx, int desty)
- {
- return stbte__in_rect(x,y, destx, desty, stbte__ui.copy_width, stbte__ui.copy_height);
- }
- #endif
- static void stbte__paste(stbte_tilemap *tm, int mapx, int mapy)
- {
- int w = stbte__ui.copy_width;
- int h = stbte__ui.copy_height;
- int i,j,k,p;
- int x = mapx - (w>>1);
- int y = mapy - (h>>1);
- int copy_props = stbte__should_copy_properties(tm) && stbte__ui.copy_has_props;
- if (stbte__ui.has_copy == 0)
- return;
- stbte__begin_undo(tm);
- p = 0;
- for (j=0; j < h; ++j) {
- for (i=0; i < w; ++i) {
- if (y+j >= 0 && y+j < tm->max_y && x+i >= 0 && x+i < tm->max_x) {
- // compute the new stack
- short tilestack[STBTE_MAX_LAYERS];
- for (k=0; k < tm->num_layers; ++k)
- tilestack[k] = tm->data[y+j][x+i][k];
- stbte__paste_stack(tm, tilestack, tilestack, stbte__ui.copybuffer[p], 0);
- // update anything that changed
- for (k=0; k < tm->num_layers; ++k) {
- if (tilestack[k] != tm->data[y+j][x+i][k]) {
- stbte__undo_record(tm, x+i,y+j,k, tm->data[y+j][x+i][k]);
- tm->data[y+j][x+i][k] = tilestack[k];
- }
- }
- }
- if (copy_props) {
- #ifdef STBTE_ALLOW_LINK
- // need to decide how to paste a link, so there's a few cases
- int destx = -1, desty = -1;
- stbte__link *link = &stbte__ui.copylinks[p];
- // check if link is within-rect
- if (stbte__in_src_rect(link->x, link->y)) {
- // new link should point to copy (but only if copy is within map)
- destx = x + (link->x - stbte__ui.copy_src_x);
- desty = y + (link->y - stbte__ui.copy_src_y);
- } else if (tm == stbte__ui.copy_src) {
- // if same map, then preserve link unless target is overwritten
- if (!stbte__in_dest_rect(link->x,link->y,x,y)) {
- destx = link->x;
- desty = link->y;
- }
- }
- // this is necessary for offset-copy, but also in case max_x/max_y has changed
- if (destx < 0 || destx >= tm->max_x || desty < 0 || desty >= tm->max_y)
- destx = -1, desty = -1;
- stbte__set_link(tm, x+i, y+j, destx, desty, STBTE__undo_record);
- #endif
- for (k=0; k < STBTE_MAX_PROPERTIES; ++k) {
- if (tm->props[y+j][x+i][k] != stbte__ui.copyprops[p][k])
- stbte__undo_record_prop_float(tm, x+i, y+j, k, tm->props[y+j][x+i][k]);
- }
- stbte__copy_properties(tm->props[y+j][x+i], stbte__ui.copyprops[p]);
- }
- ++p;
- }
- }
- stbte__end_undo(tm);
- }
- static void stbte__drag_update(stbte_tilemap *tm, int mapx, int mapy, int copy_props)
- {
- int w = stbte__ui.drag_w, h = stbte__ui.drag_h;
- int ox,oy,i,deleted=0,written=0;
- short temp[STBTE_MAX_LAYERS];
- short *data = NULL;
- STBTE__NOTUSED(deleted);
- STBTE__NOTUSED(written);
- if (!stbte__ui.shift) {
- ox = mapx - stbte__ui.drag_x;
- oy = mapy - stbte__ui.drag_y;
- if (ox >= 0 && ox < w && oy >= 0 && oy < h) {
- deleted=1;
- for (i=0; i < tm->num_layers; ++i)
- temp[i] = tm->data[mapy][mapx][i];
- data = temp;
- stbte__clear_stack(tm, data);
- }
- }
- ox = mapx - stbte__ui.drag_dest_x;
- oy = mapy - stbte__ui.drag_dest_y;
- // if this map square is in the target drag region
- if (ox >= 0 && ox < w && oy >= 0 && oy < h) {
- // and the src map square is on the map
- if (stbte__in_rect(stbte__ui.drag_x+ox, stbte__ui.drag_y+oy, 0, 0, tm->max_x, tm->max_y)) {
- written = 1;
- if (data == NULL) {
- for (i=0; i < tm->num_layers; ++i)
- temp[i] = tm->data[mapy][mapx][i];
- data = temp;
- }
- stbte__paste_stack(tm, data, data, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift);
- if (copy_props) {
- for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
- if (tm->props[mapy][mapx][i] != tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i]) {
- stbte__undo_record_prop_float(tm, mapx, mapy, i, tm->props[mapy][mapx][i]);
- tm->props[mapy][mapx][i] = tm->props[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox][i];
- }
- }
- }
- }
- }
- if (data) {
- for (i=0; i < tm->num_layers; ++i) {
- if (tm->data[mapy][mapx][i] != data[i]) {
- stbte__undo_record(tm, mapx, mapy, i, tm->data[mapy][mapx][i]);
- tm->data[mapy][mapx][i] = data[i];
- }
- }
- }
- #ifdef STBTE_ALLOW_LINK
- if (copy_props) {
- int overwritten=0, moved=0, copied=0;
- // since this function is called on EVERY tile, we can fix up even tiles not
- // involved in the move
- stbte__link *k;
- // first, determine what src link ends up here
- k = &tm->link[mapy][mapx]; // by default, it's the one currently here
- if (deleted) // if dragged away, it's erased
- k = NULL;
- if (written) // if dragged into, it gets that link
- k = &tm->link[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox];
- // now check whether the *target* gets moved or overwritten
- if (k && k->x >= 0) {
- overwritten = stbte__in_rect(k->x, k->y, stbte__ui.drag_dest_x, stbte__ui.drag_dest_y, w, h);
- if (!stbte__ui.shift)
- moved = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h);
- else
- copied = stbte__in_rect(k->x, k->y, stbte__ui.drag_x , stbte__ui.drag_y , w, h);
- }
- if (deleted || written || overwritten || moved || copied) {
- // choose the final link value based on the above
- if (k == NULL || k->x < 0)
- stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record);
- else if (moved || (copied && written)) {
- // if we move the target, we update to point to the new target;
- // or, if we copy the target and the source is part of the copy, then update to new target
- int x = k->x + (stbte__ui.drag_dest_x - stbte__ui.drag_x);
- int y = k->y + (stbte__ui.drag_dest_y - stbte__ui.drag_y);
- if (!(x >= 0 && y >= 0 && x < tm->max_x && y < tm->max_y))
- x = -1, y = -1;
- stbte__set_link(tm, mapx, mapy, x, y, STBTE__undo_record);
- } else if (overwritten) {
- stbte__set_link(tm, mapx, mapy, -1, -1, STBTE__undo_record);
- } else
- stbte__set_link(tm, mapx, mapy, k->x, k->y, STBTE__undo_record);
- }
- }
- #endif
- }
- static void stbte__drag_place(stbte_tilemap *tm, int mapx, int mapy)
- {
- int i,j;
- int copy_props = stbte__should_copy_properties(tm);
- int move_x = (stbte__ui.drag_dest_x - stbte__ui.drag_x);
- int move_y = (stbte__ui.drag_dest_y - stbte__ui.drag_y);
- if (move_x == 0 && move_y == 0)
- return;
- stbte__begin_undo(tm);
- // we now need a 2D memmove-style mover that doesn't
- // overwrite any data as it goes. this requires being
- // direction sensitive in the same way as memmove
- if (move_y > 0 || (move_y == 0 && move_x > 0)) {
- for (j=tm->max_y-1; j >= 0; --j)
- for (i=tm->max_x-1; i >= 0; --i)
- stbte__drag_update(tm,i,j,copy_props);
- } else {
- for (j=0; j < tm->max_y; ++j)
- for (i=0; i < tm->max_x; ++i)
- stbte__drag_update(tm,i,j,copy_props);
- }
- stbte__end_undo(tm);
- stbte__ui.has_selection = 1;
- stbte__ui.select_x0 = stbte__ui.drag_dest_x;
- stbte__ui.select_y0 = stbte__ui.drag_dest_y;
- stbte__ui.select_x1 = stbte__ui.select_x0 + stbte__ui.drag_w - 1;
- stbte__ui.select_y1 = stbte__ui.select_y0 + stbte__ui.drag_h - 1;
- }
- static void stbte__tile_paint(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy, int layer)
- {
- int i;
- int id = STBTE__IDMAP(mapx,mapy);
- int x0=sx, y0=sy;
- int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
- stbte__hittest(x0,y0,x1,y1, id);
- short *data = tm->data[mapy][mapx];
- short temp[STBTE_MAX_LAYERS];
- if (STBTE__IS_MAP_HOT()) {
- if (stbte__ui.pasting) {
- int ox = mapx - stbte__ui.paste_x;
- int oy = mapy - stbte__ui.paste_y;
- if (ox >= 0 && ox < stbte__ui.copy_width && oy >= 0 && oy < stbte__ui.copy_height) {
- stbte__paste_stack(tm, temp, tm->data[mapy][mapx], stbte__ui.copybuffer[oy*stbte__ui.copy_width+ox], 0);
- data = temp;
- }
- } else if (stbte__ui.dragging) {
- int ox,oy;
- for (i=0; i < tm->num_layers; ++i)
- temp[i] = tm->data[mapy][mapx][i];
- data = temp;
- // if it's in the source area, remove things unless shift-dragging
- ox = mapx - stbte__ui.drag_x;
- oy = mapy - stbte__ui.drag_y;
- if (!stbte__ui.shift && ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) {
- stbte__clear_stack(tm, temp);
- }
- ox = mapx - stbte__ui.drag_dest_x;
- oy = mapy - stbte__ui.drag_dest_y;
- if (ox >= 0 && ox < stbte__ui.drag_w && oy >= 0 && oy < stbte__ui.drag_h) {
- stbte__paste_stack(tm, temp, temp, tm->data[stbte__ui.drag_y+oy][stbte__ui.drag_x+ox], !stbte__ui.shift);
- }
- } else if (STBTE__IS_MAP_ACTIVE()) {
- if (stbte__ui.tool == STBTE__tool_rect) {
- if ((stbte__ui.ms_time & 511) < 380) {
- int ex = ((stbte__ui.hot_id >> 19) & 4095);
- int ey = ((stbte__ui.hot_id >> 7) & 4095);
- int sx = stbte__ui.sx;
- int sy = stbte__ui.sy;
- if ( ((mapx >= sx && mapx < ex+1) || (mapx >= ex && mapx < sx+1))
- && ((mapy >= sy && mapy < ey+1) || (mapy >= ey && mapy < sy+1))) {
- int i;
- for (i=0; i < tm->num_layers; ++i)
- temp[i] = tm->data[mapy][mapx][i];
- data = temp;
- if (stbte__ui.active_event == STBTE__leftdown)
- stbte__brush_predict(tm, temp);
- else
- stbte__erase_predict(tm, temp, STBTE__erase_any);
- }
- }
- }
- }
- }
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE() && !stbte__ui.pasting) {
- if (stbte__ui.tool == STBTE__tool_brush) {
- if ((stbte__ui.ms_time & 511) < 300) {
- data = temp;
- for (i=0; i < tm->num_layers; ++i)
- temp[i] = tm->data[mapy][mapx][i];
- stbte__brush_predict(tm, temp);
- }
- }
- }
- {
- i = layer;
- if (i == tm->solo_layer || (!tm->layerinfo[i].hidden && tm->solo_layer < 0))
- if (data[i] >= 0)
- STBTE_DRAW_TILE(sx,sy, (unsigned short) data[i], 0, tm->props[mapy][mapx]);
- }
- }
- static void stbte__tile(stbte_tilemap *tm, int sx, int sy, int mapx, int mapy)
- {
- int tool = stbte__ui.tool;
- int x0=sx, y0=sy;
- int x1=sx+tm->spacing_x, y1=sy+tm->spacing_y;
- int id = STBTE__IDMAP(mapx,mapy);
- int over = stbte__hittest(x0,y0,x1,y1, id);
- switch (stbte__ui.event) {
- case STBTE__paint: {
- if (stbte__ui.pasting || stbte__ui.dragging || stbte__ui.scrolling)
- break;
- if (stbte__ui.scrollkey && !STBTE__IS_MAP_ACTIVE())
- break;
- if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE() && (tool == STBTE__tool_rect || tool == STBTE__tool_select)) {
- int rx0,ry0,rx1,ry1,t;
- // compute the center of each rect
- rx0 = x0 + tm->spacing_x/2;
- ry0 = y0 + tm->spacing_y/2;
- rx1 = rx0 + (stbte__ui.sx - mapx) * tm->spacing_x;
- ry1 = ry0 + (stbte__ui.sy - mapy) * tm->spacing_y;
- if (rx0 > rx1) t=rx0,rx0=rx1,rx1=t;
- if (ry0 > ry1) t=ry0,ry0=ry1,ry1=t;
- rx0 -= tm->spacing_x/2;
- ry0 -= tm->spacing_y/2;
- rx1 += tm->spacing_x/2;
- ry1 += tm->spacing_y/2;
- stbte__draw_frame(rx0-1,ry0-1,rx1+1,ry1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT);
- break;
- }
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
- stbte__draw_frame(x0-1,y0-1,x1+1,y1+1, STBTE_COLOR_TILEMAP_HIGHLIGHT);
- }
- #ifdef STBTE_ALLOW_LINK
- if (stbte__ui.show_links && tm->link[mapy][mapx].x >= 0) {
- int tx = tm->link[mapy][mapx].x;
- int ty = tm->link[mapy][mapx].y;
- int lx0,ly0,lx1,ly1;
- if (STBTE_ALLOW_LINK(tm->data[mapy][mapx], tm->props[mapy][mapx],
- tm->data[ty ][tx ], tm->props[ty ][tx ]))
- {
- lx0 = x0 + (tm->spacing_x >> 1) - 1;
- ly0 = y0 + (tm->spacing_y >> 1) - 1;
- lx1 = lx0 + (tx - mapx) * tm->spacing_x + 2;
- ly1 = ly0 + (ty - mapy) * tm->spacing_y + 2;
- stbte__draw_link(lx0,ly0,lx1,ly1,
- STBTE_LINK_COLOR(tm->data[mapy][mapx], tm->props[mapy][mapx],
- tm->data[ty ][tx ], tm->props[ty ][tx]));
- }
- }
- #endif
- break;
- }
- }
- if (stbte__ui.pasting) {
- switch (stbte__ui.event) {
- case STBTE__leftdown:
- if (STBTE__IS_HOT(id)) {
- stbte__ui.pasting = 0;
- stbte__paste(tm, mapx, mapy);
- stbte__activate(0);
- }
- break;
- case STBTE__leftup:
- // just clear it no matter what, since they might click away to clear it
- stbte__activate(0);
- break;
- case STBTE__rightdown:
- if (STBTE__IS_HOT(id)) {
- stbte__activate(0);
- stbte__ui.pasting = 0;
- }
- break;
- }
- return;
- }
- if (stbte__ui.scrolling) {
- if (stbte__ui.event == STBTE__leftup) {
- stbte__activate(0);
- stbte__ui.scrolling = 0;
- }
- if (stbte__ui.event == STBTE__mousemove) {
- tm->scroll_x += (stbte__ui.start_x - stbte__ui.mx);
- tm->scroll_y += (stbte__ui.start_y - stbte__ui.my);
- stbte__ui.start_x = stbte__ui.mx;
- stbte__ui.start_y = stbte__ui.my;
- }
- return;
- }
- // regardless of tool, leftdown is a scrolldrag
- if (STBTE__IS_HOT(id) && stbte__ui.scrollkey && stbte__ui.event == STBTE__leftdown) {
- stbte__ui.scrolling = 1;
- stbte__ui.start_x = stbte__ui.mx;
- stbte__ui.start_y = stbte__ui.my;
- return;
- }
- switch (tool) {
- case STBTE__tool_brush:
- switch (stbte__ui.event) {
- case STBTE__mousemove:
- if (STBTE__IS_MAP_ACTIVE() && over) {
- // 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
- if (!STBTE__IS_ACTIVE(id)) {
- if (stbte__ui.active_event == STBTE__leftdown)
- stbte__brush(tm, mapx, mapy);
- else
- stbte__erase(tm, mapx, mapy, stbte__ui.brush_state);
- stbte__ui.active_id = id; // switch to this map square so we don't rebrush IT multiple times
- }
- }
- break;
- case STBTE__leftdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
- stbte__activate(id);
- stbte__begin_undo(tm);
- stbte__brush(tm, mapx, mapy);
- }
- break;
- case STBTE__rightdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
- stbte__activate(id);
- stbte__begin_undo(tm);
- if (stbte__erase(tm, mapx, mapy, STBTE__erase_any) == STBTE__erase_brushonly)
- stbte__ui.brush_state = STBTE__erase_brushonly;
- else
- stbte__ui.brush_state = STBTE__erase_any;
- }
- break;
- case STBTE__leftup:
- case STBTE__rightup:
- if (STBTE__IS_MAP_ACTIVE()) {
- stbte__end_undo(tm);
- stbte__activate(0);
- }
- break;
- }
- break;
- #ifdef STBTE_ALLOW_LINK
- case STBTE__tool_link:
- switch (stbte__ui.event) {
- case STBTE__leftdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
- stbte__activate(id);
- stbte__ui.linking = 1;
- stbte__ui.sx = mapx;
- stbte__ui.sy = mapy;
- // @TODO: undo
- }
- break;
- case STBTE__leftup:
- if (STBTE__IS_HOT(id) && STBTE__IS_MAP_ACTIVE()) {
- if ((mapx != stbte__ui.sx || mapy != stbte__ui.sy) &&
- STBTE_ALLOW_LINK(tm->data[stbte__ui.sy][stbte__ui.sx], tm->props[stbte__ui.sy][stbte__ui.sx],
- tm->data[mapy][mapx], tm->props[mapy][mapx]))
- stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, STBTE__undo_block);
- else
- stbte__set_link(tm, stbte__ui.sx, stbte__ui.sy, -1,-1, STBTE__undo_block);
- stbte__ui.linking = 0;
- stbte__activate(0);
- }
- break;
- case STBTE__rightdown:
- if (STBTE__IS_ACTIVE(id)) {
- stbte__activate(0);
- stbte__ui.linking = 0;
- }
- break;
- }
- break;
- #endif
- case STBTE__tool_erase:
- switch (stbte__ui.event) {
- case STBTE__mousemove:
- if (STBTE__IS_MAP_ACTIVE() && over)
- stbte__erase(tm, mapx, mapy, STBTE__erase_all);
- break;
- case STBTE__leftdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE()) {
- stbte__activate(id);
- stbte__begin_undo(tm);
- stbte__erase(tm, mapx, mapy, STBTE__erase_all);
- }
- break;
- case STBTE__leftup:
- if (STBTE__IS_MAP_ACTIVE()) {
- stbte__end_undo(tm);
- stbte__activate(0);
- }
- break;
- }
- break;
- case STBTE__tool_select:
- if (STBTE__IS_HOT(id)) {
- switch (stbte__ui.event) {
- case STBTE__leftdown:
- if (STBTE__INACTIVE()) {
- // if we're clicking in an existing selection...
- if (stbte__ui.has_selection) {
- if ( mapx >= stbte__ui.select_x0 && mapx <= stbte__ui.select_x1
- && mapy >= stbte__ui.select_y0 && mapy <= stbte__ui.select_y1)
- {
- stbte__ui.dragging = 1;
- stbte__ui.drag_x = stbte__ui.select_x0;
- stbte__ui.drag_y = stbte__ui.select_y0;
- stbte__ui.drag_w = stbte__ui.select_x1 - stbte__ui.select_x0 + 1;
- stbte__ui.drag_h = stbte__ui.select_y1 - stbte__ui.select_y0 + 1;
- stbte__ui.drag_offx = mapx - stbte__ui.select_x0;
- stbte__ui.drag_offy = mapy - stbte__ui.select_y0;
- }
- }
- stbte__ui.has_selection = 0; // no selection until it completes
- stbte__activate_map(mapx,mapy);
- }
- break;
- case STBTE__leftup:
- if (STBTE__IS_MAP_ACTIVE()) {
- if (stbte__ui.dragging) {
- stbte__drag_place(tm, mapx,mapy);
- stbte__ui.dragging = 0;
- stbte__activate(0);
- } else {
- stbte__select_rect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy);
- stbte__activate(0);
- }
- }
- break;
- case STBTE__rightdown:
- stbte__ui.has_selection = 0;
- break;
- }
- }
- break;
- case STBTE__tool_rect:
- if (STBTE__IS_HOT(id)) {
- switch (stbte__ui.event) {
- case STBTE__leftdown:
- if (STBTE__INACTIVE())
- stbte__activate_map(mapx,mapy);
- break;
- case STBTE__leftup:
- if (STBTE__IS_MAP_ACTIVE()) {
- stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 1);
- stbte__activate(0);
- }
- break;
- case STBTE__rightdown:
- if (STBTE__INACTIVE())
- stbte__activate_map(mapx,mapy);
- break;
- case STBTE__rightup:
- if (STBTE__IS_MAP_ACTIVE()) {
- stbte__fillrect(tm, stbte__ui.sx, stbte__ui.sy, mapx, mapy, 0);
- stbte__activate(0);
- }
- break;
- }
- }
- break;
- case STBTE__tool_eyedrop:
- switch (stbte__ui.event) {
- case STBTE__leftdown:
- if (STBTE__IS_HOT(id) && STBTE__INACTIVE())
- stbte__eyedrop(tm,mapx,mapy);
- break;
- }
- break;
- }
- }
- static void stbte__start_paste(stbte_tilemap *tm)
- {
- if (stbte__ui.has_copy) {
- stbte__ui.pasting = 1;
- stbte__activate(STBTE__ID(STBTE__toolbarB,3));
- }
- }
- static void stbte__toolbar(stbte_tilemap *tm, int x0, int y0, int w, int h)
- {
- int i;
- int estimated_width = 13 * STBTE__num_tool + 8+8+ 120+4 - 30;
- int x = x0 + w/2 - estimated_width/2;
- int y = y0+1;
- for (i=0; i < STBTE__num_tool; ++i) {
- int highlight=0, disable=0;
- highlight = (stbte__ui.tool == i);
- if (i == STBTE__tool_undo || i == STBTE__tool_showgrid)
- x += 8;
- if (i == STBTE__tool_showgrid && stbte__ui.show_grid)
- highlight = 1;
- if (i == STBTE__tool_showlinks && stbte__ui.show_links)
- highlight = 1;
- if (i == STBTE__tool_fill)
- continue;
- #ifndef STBTE_ALLOW_LINK
- if (i == STBTE__tool_link || i == STBTE__tool_showlinks)
- disable = 1;
- #endif
- if (i == STBTE__tool_undo && !stbte__undo_available(tm))
- disable = 1;
- if (i == STBTE__tool_redo && !stbte__redo_available(tm))
- disable = 1;
- if (stbte__button_icon(STBTE__ctoolbar_button, toolchar[i], x, y, 13, STBTE__ID(STBTE__toolbarA, i), highlight, disable)) {
- switch (i) {
- case STBTE__tool_eyedrop:
- stbte__ui.eyedrop_last_layer = tm->num_layers; // flush eyedropper state
- // fallthrough
- default:
- stbte__ui.tool = i;
- stbte__ui.has_selection = 0;
- break;
- case STBTE__tool_showlinks:
- stbte__ui.show_links = !stbte__ui.show_links;
- break;
- case STBTE__tool_showgrid:
- stbte__ui.show_grid = (stbte__ui.show_grid+1)%3;
- break;
- case STBTE__tool_undo:
- stbte__undo(tm);
- break;
- case STBTE__tool_redo:
- stbte__redo(tm);
- break;
- }
- }
- x += 13;
- }
- x += 8;
- if (stbte__button(STBTE__ctoolbar_button, "cut" , x, y,10, 40, STBTE__ID(STBTE__toolbarB,0), 0, !stbte__ui.has_selection))
- stbte__copy_cut(tm, 1);
- x += 42;
- if (stbte__button(STBTE__ctoolbar_button, "copy" , x, y, 5, 40, STBTE__ID(STBTE__toolbarB,1), 0, !stbte__ui.has_selection))
- stbte__copy_cut(tm, 0);
- x += 42;
- if (stbte__button(STBTE__ctoolbar_button, "paste", x, y, 0, 40, STBTE__ID(STBTE__toolbarB,2), stbte__ui.pasting, !stbte__ui.has_copy))
- stbte__start_paste(tm);
- }
- #define STBTE__TEXTCOLOR(n) stbte__color_table[n][STBTE__text][STBTE__idle]
- static int stbte__info_value(const char *label, int x, int y, int val, int digits, int id)
- {
- if (stbte__ui.event == STBTE__paint) {
- int off = 9-stbte__get_char_width(label[0]);
- char text[16];
- stbte__sprintf(text stbte__sizeof(text), label, digits, val);
- stbte__draw_text_core(x+off,y, text, 999, STBTE__TEXTCOLOR(STBTE__cpanel),1);
- }
- if (id) {
- x += 9+7*digits+4;
- if (stbte__minibutton(STBTE__cmapsize, x,y, '+', STBTE__ID2(id,1,0)))
- val += (stbte__ui.shift ? 10 : 1);
- x += 9;
- if (stbte__minibutton(STBTE__cmapsize, x,y, '-', STBTE__ID2(id,2,0)))
- val -= (stbte__ui.shift ? 10 : 1);
- if (val < 1) val = 1; else if (val > 4096) val = 4096;
- }
- return val;
- }
- static void stbte__info(stbte_tilemap *tm, int x0, int y0, int w, int h)
- {
- int mode = stbte__ui.panel[STBTE__panel_info].mode;
- int s = 11+7*tm->digits+4+15;
- int x,y;
- int in_region;
- x = x0+2;
- y = y0+2;
- tm->max_x = stbte__info_value("w:%*d",x,y, tm->max_x, tm->digits, STBTE__ID(STBTE__info,0));
- if (mode)
- x += s;
- else
- y += 11;
- tm->max_y = stbte__info_value("h:%*d",x,y, tm->max_y, tm->digits, STBTE__ID(STBTE__info,1));
- x = x0+2;
- y += 11;
- in_region = (stbte__ui.hot_id & 127) == STBTE__map;
- stbte__info_value(in_region ? "x:%*d" : "x:",x,y, (stbte__ui.hot_id>>19)&4095, tm->digits, 0);
- if (mode)
- x += s;
- else
- y += 11;
- stbte__info_value(in_region ? "y:%*d" : "y:",x,y, (stbte__ui.hot_id>> 7)&4095, tm->digits, 0);
- y += 15;
- x = x0+2;
- stbte__draw_text(x,y,"brush:",40,STBTE__TEXTCOLOR(STBTE__cpanel));
- if (tm->cur_tile >= 0)
- STBTE_DRAW_TILE(x+43,y-3,tm->tiles[tm->cur_tile].id,1,0);
- }
- static void stbte__layers(stbte_tilemap *tm, int x0, int y0, int w, int h)
- {
- static const char *propmodes[3] = {
- "default", "always", "never"
- };
- int num_rows;
- int i, y, n;
- int x1 = x0+w;
- int y1 = y0+h;
- int xoff = 20;
- if (tm->has_layer_names) {
- int side = stbte__ui.panel[STBTE__panel_layers].side;
- xoff = stbte__region[side].width - 42;
- xoff = (xoff < tm->layername_width + 10 ? xoff : tm->layername_width + 10);
- }
- x0 += 2;
- y0 += 5;
- if (!tm->has_layer_names) {
- if (stbte__ui.event == STBTE__paint) {
- stbte__draw_text(x0,y0, "Layers", w-4, STBTE__TEXTCOLOR(STBTE__cpanel));
- }
- y0 += 11;
- }
- num_rows = (y1-y0)/15;
- #ifndef STBTE_NO_PROPS
- --num_rows;
- #endif
- y = y0;
- for (i=0; i < tm->num_layers; ++i) {
- char text[3], *str = (char *) tm->layerinfo[i].name;
- static char lockedchar[3] = { 'U', 'P', 'L' };
- int locked = tm->layerinfo[i].locked;
- int disabled = (tm->solo_layer >= 0 && tm->solo_layer != i);
- if (i-tm->layer_scroll >= 0 && i-tm->layer_scroll < num_rows) {
- if (str == NULL)
- stbte__sprintf(str=text stbte__sizeof(text), "%2d", i+1);
- 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))
- tm->cur_layer = (tm->cur_layer == i ? -1 : i);
- if (stbte__layerbutton(x0+xoff + 0,y+1,'H',STBTE__ID(STBTE__hide,i), tm->layerinfo[i].hidden,disabled,STBTE__clayer_hide))
- tm->layerinfo[i].hidden = !tm->layerinfo[i].hidden;
- if (stbte__layerbutton(x0+xoff + 12,y+1,lockedchar[locked],STBTE__ID(STBTE__lock,i), locked!=0,disabled,STBTE__clayer_lock))
- tm->layerinfo[i].locked = (locked+1)%3;
- if (stbte__layerbutton(x0+xoff + 24,y+1,'S',STBTE__ID(STBTE__solo,i), tm->solo_layer==i,0,STBTE__clayer_solo))
- tm->solo_layer = (tm->solo_layer == i ? -1 : i);
- y += 15;
- }
- }
- stbte__scrollbar(x1-4, y0,y-2, &tm->layer_scroll, 0, tm->num_layers, num_rows, STBTE__ID(STBTE__scrollbar_id, STBTE__layer));
- #ifndef STBTE_NO_PROPS
- n = stbte__text_width("prop:")+2;
- stbte__draw_text(x0,y+2, "prop:", w, STBTE__TEXTCOLOR(STBTE__cpanel));
- i = w - n - 4;
- if (i > 50) i = 50;
- if (stbte__button(STBTE__clayer_button, propmodes[tm->propmode], x0+n,y,0,i, STBTE__ID(STBTE__layer,256), 0,0))
- tm->propmode = (tm->propmode+1)%3;
- #endif
- }
- static void stbte__categories(stbte_tilemap *tm, int x0, int y0, int w, int h)
- {
- int s=11, x,y, i;
- int num_rows = h / s;
- w -= 4;
- x = x0+2;
- y = y0+4;
- if (tm->category_scroll == 0) {
- if (stbte__category_button("*ALL*", x,y, w, STBTE__ID(STBTE__categories, 65535), tm->cur_category == -1)) {
- stbte__choose_category(tm, -1);
- }
- y += s;
- }
- for (i=0; i < tm->num_categories; ++i) {
- if (i+1 - tm->category_scroll >= 0 && i+1 - tm->category_scroll < num_rows) {
- if (y + 10 > y0+h)
- return;
- if (stbte__category_button(tm->categories[i], x,y,w, STBTE__ID(STBTE__categories,i), tm->cur_category == i))
- stbte__choose_category(tm, i);
- y += s;
- }
- }
- 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));
- }
- static void stbte__tile_in_palette(stbte_tilemap *tm, int x, int y, int slot)
- {
- stbte__tileinfo *t = &tm->tiles[slot];
- int x0=x, y0=y, x1 = x+tm->palette_spacing_x - 1, y1 = y+tm->palette_spacing_y;
- int id = STBTE__ID(STBTE__palette, slot);
- stbte__hittest(x0,y0,x1,y1, id);
- switch (stbte__ui.event) {
- case STBTE__paint:
- stbte__draw_rect(x,y,x+tm->palette_spacing_x-1,y+tm->palette_spacing_x-1, STBTE_COLOR_TILEPALETTE_BACKGROUND);
- STBTE_DRAW_TILE(x,y,id, slot == tm->cur_tile,0);
- if (slot == tm->cur_tile)
- stbte__draw_frame_delayed(x-1,y-1,x+tm->palette_spacing_x,y+tm->palette_spacing_y, STBTE_COLOR_TILEPALETTE_OUTLINE);
- break;
- default:
- if (stbte__button_core(id))
- tm->cur_tile = slot;
- break;
- }
- }
- static void stbte__palette_of_tiles(stbte_tilemap *tm, int x0, int y0, int w, int h)
- {
- int i,x,y;
- int num_vis_rows = (h-6) / tm->palette_spacing_y;
- int num_columns = (w-2-6) / tm->palette_spacing_x;
- int num_total_rows;
- int column,row;
- int x1 = x0+w, y1=y0+h;
- x = x0+2;
- y = y0+6;
- if (num_columns == 0)
- return;
- num_total_rows = (tm->cur_palette_count + num_columns-1) / num_columns; // ceil()
- column = 0;
- row = -tm->palette_scroll;
- for (i=0; i < tm->num_tiles; ++i) {
- stbte__tileinfo *t = &tm->tiles[i];
- // filter based on category
- if (tm->cur_category >= 0 && t->category_id != tm->cur_category)
- continue;
- // display it
- if (row >= 0 && row < num_vis_rows) {
- x = x0 + 2 + tm->palette_spacing_x * column;
- y = y0 + 6 + tm->palette_spacing_y * row;
- stbte__tile_in_palette(tm,x,y,i);
- }
- ++column;
- if (column == num_columns) {
- column = 0;
- ++row;
- }
- }
- stbte__flush_delay();
- 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));
- }
- static float stbte__saved;
- static void stbte__props_panel(stbte_tilemap *tm, int x0, int y0, int w, int h)
- {
- int x1 = x0+w;
- int i;
- int y = y0 + 5, x = x0+2;
- int slider_width = 60;
- int mx,my;
- float *p;
- short *data;
- if (!stbte__is_single_selection())
- return;
- mx = stbte__ui.select_x0;
- my = stbte__ui.select_y0;
- p = tm->props[my][mx];
- data = tm->data[my][mx];
- STBTE__NOTUSED(data);
- for (i=0; i < STBTE_MAX_PROPERTIES; ++i) {
- unsigned int n = STBTE_PROP_TYPE(i, data, p);
- if (n) {
- char *s = (char*) STBTE_PROP_NAME(i, data, p);
- if (s == NULL) s = (char*) "";
- switch (n & 3) {
- case STBTE_PROP_bool: {
- int flag = (int) p[i];
- if (stbte__layerbutton(x,y, flag ? 'x' : ' ', STBTE__ID(STBTE__prop_flag,i), flag, 0, 2)) {
- stbte__begin_undo(tm);
- stbte__undo_record_prop_float(tm,mx,my,i,(float) flag);
- p[i] = (float) !flag;
- stbte__end_undo(tm);
- }
- stbte__draw_text(x+13,y+1,s,x1-(x+13)-2,STBTE__TEXTCOLOR(STBTE__cpanel));
- y += 13;
- break;
- }
- case STBTE_PROP_int: {
- int a = (int) STBTE_PROP_MIN(i,data,p);
- int b = (int) STBTE_PROP_MAX(i,data,p);
- int v = (int) p[i] - a;
- if (a+v != p[i] || v < 0 || v > b-a) {
- if (v < 0) v = 0;
- if (v > b-a) v = b-a;
- p[i] = (float) (a+v); // @TODO undo
- }
- switch (stbte__slider(x, slider_width, y+7, b-a, &v, STBTE__ID(STBTE__prop_int,i)))
- {
- case STBTE__begin:
- stbte__saved = p[i];
- // fallthrough
- case STBTE__change:
- p[i] = (float) (a+v); // @TODO undo
- break;
- case STBTE__end:
- if (p[i] != stbte__saved) {
- stbte__begin_undo(tm);
- stbte__undo_record_prop_float(tm,mx,my,i,stbte__saved);
- stbte__end_undo(tm);
- }
- break;
- }
- stbte__draw_text(x+slider_width+2,y+2, s, x1-1-(x+slider_width+2), STBTE__TEXTCOLOR(STBTE__cpanel));
- y += 12;
- break;
- }
- case STBTE_PROP_float: {
- float a = (float) STBTE_PROP_MIN(i, data,p);
- float b = (float) STBTE_PROP_MAX(i, data,p);
- float c = STBTE_PROP_FLOAT_SCALE(i, data, p);
- float old;
- if (p[i] < a || p[i] > b) {
- // @TODO undo
- if (p[i] < a) p[i] = a;
- if (p[i] > b) p[i] = b;
- }
- old = p[i];
- switch (stbte__float_control(x, y, 50, a, b, c, "%8.4f", &p[i], STBTE__layer,STBTE__ID(STBTE__prop_float,i))) {
- case STBTE__begin:
- stbte__saved = old;
- break;
- case STBTE__end:
- if (stbte__saved != p[i]) {
- stbte__begin_undo(tm);
- stbte__undo_record_prop_float(tm,mx,my,i, stbte__saved);
- stbte__end_undo(tm);
- }
- break;
- }
- stbte__draw_text(x+53,y+1, s, x1-1-(x+53), STBTE__TEXTCOLOR(STBTE__cpanel));
- y += 12;
- break;
- }
- }
- }
- }
- }
- static int stbte__cp_mode, stbte__cp_aspect, stbte__save, stbte__cp_altered;
- #ifdef STBTE__COLORPICKER
- static int stbte__cp_state, stbte__cp_index, stbte__color_copy;
- static void stbte__dump_colorstate(void)
- {
- int i,j,k;
- printf("static int stbte__color_table[STBTE__num_color_modes][STBTE__num_color_aspects][STBTE__num_color_states] =\n");
- printf("{\n");
- printf(" {\n");
- for (k=0; k < STBTE__num_color_modes; ++k) {
- for (j=0; j < STBTE__num_color_aspects; ++j) {
- printf(" { ");
- for (i=0; i < STBTE__num_color_states; ++i) {
- printf("0x%06x, ", stbte__color_table[k][j][i]);
- }
- printf("},\n");
- }
- if (k+1 < STBTE__num_color_modes)
- printf(" }, {\n");
- else
- printf(" },\n");
- }
- printf("};\n");
- }
- static void stbte__colorpicker(int x0, int y0, int w, int h)
- {
- int x1 = x0+w, y1 = y0+h, x,y, i;
- x = x0+2; y = y0+6;
- y += 5;
- x += 8;
- {
- int color = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index];
- int rgb[3];
- if (stbte__cp_altered && stbte__cp_index == STBTE__idle)
- color = stbte__save;
- if (stbte__minibutton(STBTE__cmapsize, x1-20,y+ 5, 'C', STBTE__ID2(STBTE__colorpick_id,4,0)))
- stbte__color_copy = color;
- if (stbte__minibutton(STBTE__cmapsize, x1-20,y+15, 'P', STBTE__ID2(STBTE__colorpick_id,4,1)))
- color = stbte__color_copy;
- rgb[0] = color >> 16; rgb[1] = (color>>8)&255; rgb[2] = color & 255;
- for (i=0; i < 3; ++i) {
- if (stbte__slider(x+8,64, y, 255, rgb+i, STBTE__ID2(STBTE__colorpick_id,3,i)) > 0)
- stbte__dump_colorstate();
- y += 15;
- }
- if (stbte__ui.event != STBTE__paint && stbte__ui.event != STBTE__tick)
- stbte__color_table[stbte__cp_mode][stbte__cp_aspect][stbte__cp_index] = (rgb[0]<<16)|(rgb[1]<<8)|(rgb[2]);
- }
- y += 5;
- // states
- x = x0+2+35;
- if (stbte__ui.event == STBTE__paint) {
- static char *states[] = { "idle", "over", "down", "down&over", "selected", "selected&over", "disabled" };
- stbte__draw_text(x, y+1, states[stbte__cp_index], x1-x-1, 0xffffff);
- }
- x = x0+24; y += 12;
- for (i=3; i >= 0; --i) {
- int state = 0 != (stbte__cp_state & (1 << i));
- if (stbte__layerbutton(x,y, "OASD"[i], STBTE__ID2(STBTE__colorpick_id, 0,i), state,0, STBTE__clayer_button)) {
- stbte__cp_state ^= (1 << i);
- stbte__cp_index = stbte__state_to_index[0][0][0][stbte__cp_state];
- }
- x += 16;
- }
- x = x0+2; y += 18;
- for (i=0; i < 3; ++i) {
- static char *labels[] = { "Base", "Edge", "Text" };
- if (stbte__button(STBTE__ctoolbar_button, labels[i], x,y,0,36, STBTE__ID2(STBTE__colorpick_id,1,i), stbte__cp_aspect==i,0))
- stbte__cp_aspect = i;
- x += 40;
- }
- y += 18;
- x = x0+2;
- for (i=0; i < STBTE__num_color_modes; ++i) {
- 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))
- stbte__cp_mode = i;
- y += 12;
- }
- // make the currently selected aspect flash, unless we're actively dragging color slider etc
- if (stbte__ui.event == STBTE__tick) {
- stbte__save = stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle];
- if ((stbte__ui.active_id & 127) != STBTE__colorpick_id) {
- if ((stbte__ui.ms_time & 2047) < 200) {
- stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] ^= 0x1f1f1f;
- stbte__cp_altered = 1;
- }
- }
- }
- }
- #endif
- static void stbte__editor_traverse(stbte_tilemap *tm)
- {
- int i,j,i0,j0,i1,j1,n;
- if (tm == NULL)
- return;
- if (stbte__ui.x0 == stbte__ui.x1 || stbte__ui.y0 == stbte__ui.y1)
- return;
- stbte__prepare_tileinfo(tm);
- stbte__compute_panel_locations(tm); // @OPTIMIZE: we don't need to recompute this every time
- if (stbte__ui.event == STBTE__paint) {
- // fill screen with border
- stbte__draw_rect(stbte__ui.x0, stbte__ui.y0, stbte__ui.x1, stbte__ui.y1, STBTE_COLOR_TILEMAP_BORDER);
- // fill tilemap with tilemap background
- stbte__draw_rect(stbte__ui.x0 - tm->scroll_x, stbte__ui.y0 - tm->scroll_y,
- stbte__ui.x0 - tm->scroll_x + tm->spacing_x * tm->max_x,
- stbte__ui.y0 - tm->scroll_y + tm->spacing_y * tm->max_y, STBTE_COLOR_TILEMAP_BACKGROUND);
- }
- // step 1: traverse all the tilemap data...
- i0 = (tm->scroll_x - tm->spacing_x) / tm->spacing_x;
- j0 = (tm->scroll_y - tm->spacing_y) / tm->spacing_y;
- i1 = (tm->scroll_x + stbte__ui.x1 - stbte__ui.x0) / tm->spacing_x + 1;
- j1 = (tm->scroll_y + stbte__ui.y1 - stbte__ui.y0) / tm->spacing_y + 1;
- if (i0 < 0) i0 = 0;
- if (j0 < 0) j0 = 0;
- if (i1 > tm->max_x) i1 = tm->max_x;
- if (j1 > tm->max_y) j1 = tm->max_y;
- if (stbte__ui.event == STBTE__paint) {
- // draw all of layer 0, then all of layer 1, etc, instead of old
- // way which drew entire stack of each tile at once
- for (n=0; n < tm->num_layers; ++n) {
- for (j=j0; j < j1; ++j) {
- for (i=i0; i < i1; ++i) {
- int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x;
- int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y;
- stbte__tile_paint(tm, x, y, i, j, n);
- }
- }
- if (n == 0 && stbte__ui.show_grid == 1) {
- int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x;
- int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y;
- for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x)
- stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID);
- for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y)
- stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID);
- }
- }
- }
- if (stbte__ui.event == STBTE__paint) {
- // draw grid on top of everything except UI
- if (stbte__ui.show_grid == 2) {
- int x = stbte__ui.x0 + i0 * tm->spacing_x - tm->scroll_x;
- int y = stbte__ui.y0 + j0 * tm->spacing_y - tm->scroll_y;
- for (i=0; x < stbte__ui.x1 && i <= i1; ++i, x += tm->spacing_x)
- stbte__draw_rect(x, stbte__ui.y0, x+1, stbte__ui.y1, STBTE_COLOR_GRID);
- for (j=0; y < stbte__ui.y1 && j <= j1; ++j, y += tm->spacing_y)
- stbte__draw_rect(stbte__ui.x0, y, stbte__ui.x1, y+1, STBTE_COLOR_GRID);
- }
- }
- for (j=j0; j < j1; ++j) {
- for (i=i0; i < i1; ++i) {
- int x = stbte__ui.x0 + i * tm->spacing_x - tm->scroll_x;
- int y = stbte__ui.y0 + j * tm->spacing_y - tm->scroll_y;
- stbte__tile(tm, x, y, i, j);
- }
- }
- if (stbte__ui.event == STBTE__paint) {
- // draw the selection border
- if (stbte__ui.has_selection) {
- int x0,y0,x1,y1;
- x0 = stbte__ui.x0 + (stbte__ui.select_x0 ) * tm->spacing_x - tm->scroll_x;
- y0 = stbte__ui.y0 + (stbte__ui.select_y0 ) * tm->spacing_y - tm->scroll_y;
- x1 = stbte__ui.x0 + (stbte__ui.select_x1 + 1) * tm->spacing_x - tm->scroll_x + 1;
- y1 = stbte__ui.y0 + (stbte__ui.select_y1 + 1) * tm->spacing_y - tm->scroll_y + 1;
- stbte__draw_frame(x0,y0,x1,y1, (stbte__ui.ms_time & 256 ? STBTE_COLOR_SELECTION_OUTLINE1 : STBTE_COLOR_SELECTION_OUTLINE2));
- }
- stbte__flush_delay(); // draw a dynamic link on top of the queued links
- #ifdef STBTE_ALLOW_LINK
- if (stbte__ui.linking && STBTE__IS_MAP_HOT()) {
- int x0,y0,x1,y1;
- int color;
- int ex = ((stbte__ui.hot_id >> 19) & 4095);
- int ey = ((stbte__ui.hot_id >> 7) & 4095);
- x0 = stbte__ui.x0 + (stbte__ui.sx ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)+1;
- y0 = stbte__ui.y0 + (stbte__ui.sy ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)+1;
- x1 = stbte__ui.x0 + (ex ) * tm->spacing_x - tm->scroll_x + (tm->spacing_x>>1)-1;
- y1 = stbte__ui.y0 + (ey ) * tm->spacing_y - tm->scroll_y + (tm->spacing_y>>1)-1;
- 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]))
- color = STBTE_LINK_COLOR_DRAWING;
- else
- color = STBTE_LINK_COLOR_DISALLOWED;
- stbte__draw_link(x0,y0,x1,y1, color);
- }
- #endif
- }
- stbte__flush_delay();
- // step 2: traverse the panels
- for (i=0; i < STBTE__num_panel; ++i) {
- stbte__panel *p = &stbte__ui.panel[i];
- if (stbte__ui.event == STBTE__paint) {
- stbte__draw_box(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__cpanel, STBTE__idle);
- }
- // obscure tilemap data underneath panel
- stbte__hittest(p->x0,p->y0,p->x0+p->width,p->y0+p->height, STBTE__ID2(STBTE__panel, i, 0));
- switch (i) {
- case STBTE__panel_toolbar:
- if (stbte__ui.event == STBTE__paint)
- stbte__draw_rect(p->x0,p->y0,p->x0+p->width,p->y0+p->height, stbte__color_table[STBTE__ctoolbar][STBTE__base][STBTE__idle]);
- stbte__toolbar(tm,p->x0,p->y0,p->width,p->height);
- break;
- case STBTE__panel_info:
- stbte__info(tm,p->x0,p->y0,p->width,p->height);
- break;
- case STBTE__panel_layers:
- stbte__layers(tm,p->x0,p->y0,p->width,p->height);
- break;
- case STBTE__panel_categories:
- stbte__categories(tm,p->x0,p->y0,p->width,p->height);
- break;
- case STBTE__panel_colorpick:
- #ifdef STBTE__COLORPICKER
- stbte__colorpicker(p->x0,p->y0,p->width,p->height);
- #endif
- break;
- case STBTE__panel_tiles:
- // erase boundary between categories and tiles if they're on same side
- if (stbte__ui.event == STBTE__paint && p->side == stbte__ui.panel[STBTE__panel_categories].side)
- 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]);
- stbte__palette_of_tiles(tm,p->x0,p->y0,p->width,p->height);
- break;
- case STBTE__panel_props:
- stbte__props_panel(tm,p->x0,p->y0,p->width,p->height);
- break;
- }
- // draw the panel side selectors
- for (j=0; j < 2; ++j) {
- int result;
- if (i == STBTE__panel_toolbar) continue;
- 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);
- if (result) {
- switch (j) {
- case 0: p->side = result > 0 ? STBTE__side_left : STBTE__side_right; break;
- case 1: p->delta_height += result; break;
- }
- }
- }
- }
- if (stbte__ui.panel[STBTE__panel_categories].delta_height < -5) stbte__ui.panel[STBTE__panel_categories].delta_height = -5;
- if (stbte__ui.panel[STBTE__panel_layers ].delta_height < -5) stbte__ui.panel[STBTE__panel_layers ].delta_height = -5;
- // step 3: traverse the regions to place expander controls on them
- for (i=0; i < 2; ++i) {
- if (stbte__region[i].active) {
- int x = stbte__region[i].x;
- int width;
- if (i == STBTE__side_left)
- width = stbte__ui.left_width , x += stbte__region[i].width + 1;
- else
- width = -stbte__ui.right_width, x -= 6;
- if (stbte__microbutton_dragger(x, stbte__region[i].y+2, 5, STBTE__ID(STBTE__region,i), &width)) {
- // if non-0, it is expanding, so retract it
- if (stbte__region[i].retracted == 0.0)
- stbte__region[i].retracted = 0.01f;
- else
- stbte__region[i].retracted = 0.0;
- }
- if (i == STBTE__side_left)
- stbte__ui.left_width = width;
- else
- stbte__ui.right_width = -width;
- if (stbte__ui.event == STBTE__tick) {
- if (stbte__region[i].retracted && stbte__region[i].retracted < 1.0f) {
- stbte__region[i].retracted += stbte__ui.dt*4;
- if (stbte__region[i].retracted > 1)
- stbte__region[i].retracted = 1;
- }
- }
- }
- }
- if (stbte__ui.event == STBTE__paint && stbte__ui.alert_msg) {
- int w = stbte__text_width(stbte__ui.alert_msg);
- int x = (stbte__ui.x0+stbte__ui.x1)/2;
- int y = (stbte__ui.y0+stbte__ui.y1)*5/6;
- stbte__draw_rect (x-w/2-4,y-8, x+w/2+4,y+8, 0x604020);
- stbte__draw_frame(x-w/2-4,y-8, x+w/2+4,y+8, 0x906030);
- stbte__draw_text (x-w/2,y-4, stbte__ui.alert_msg, w+1, 0xff8040);
- }
- #ifdef STBTE_SHOW_CURSOR
- if (stbte__ui.event == STBTE__paint)
- stbte__draw_bitmap(stbte__ui.mx, stbte__ui.my, stbte__get_char_width(26), stbte__get_char_bitmap(26), 0xe0e0e0);
- #endif
- if (stbte__ui.event == STBTE__tick && stbte__ui.alert_msg) {
- stbte__ui.alert_timer -= stbte__ui.dt;
- if (stbte__ui.alert_timer < 0) {
- stbte__ui.alert_timer = 0;
- stbte__ui.alert_msg = 0;
- }
- }
- if (stbte__ui.event == STBTE__paint) {
- stbte__color_table[stbte__cp_mode][stbte__cp_aspect][STBTE__idle] = stbte__save;
- stbte__cp_altered = 0;
- }
- }
- static void stbte__do_event(stbte_tilemap *tm)
- {
- stbte__ui.next_hot_id = 0;
- stbte__editor_traverse(tm);
- stbte__ui.hot_id = stbte__ui.next_hot_id;
- // automatically cancel on mouse-up in case the object that triggered it
- // doesn't exist anymore
- if (stbte__ui.active_id) {
- if (stbte__ui.event == STBTE__leftup || stbte__ui.event == STBTE__rightup) {
- if (!stbte__ui.pasting) {
- stbte__activate(0);
- if (stbte__ui.undoing)
- stbte__end_undo(tm);
- stbte__ui.scrolling = 0;
- stbte__ui.dragging = 0;
- stbte__ui.linking = 0;
- }
- }
- }
- // we could do this stuff in the widgets directly, but it would keep recomputing
- // the same thing on every tile, which seems dumb.
- if (stbte__ui.pasting) {
- if (STBTE__IS_MAP_HOT()) {
- // compute pasting location based on last hot
- stbte__ui.paste_x = ((stbte__ui.hot_id >> 19) & 4095) - (stbte__ui.copy_width >> 1);
- stbte__ui.paste_y = ((stbte__ui.hot_id >> 7) & 4095) - (stbte__ui.copy_height >> 1);
- }
- }
- if (stbte__ui.dragging) {
- if (STBTE__IS_MAP_HOT()) {
- stbte__ui.drag_dest_x = ((stbte__ui.hot_id >> 19) & 4095) - stbte__ui.drag_offx;
- stbte__ui.drag_dest_y = ((stbte__ui.hot_id >> 7) & 4095) - stbte__ui.drag_offy;
- }
- }
- }
- static void stbte__set_event(int event, int x, int y)
- {
- stbte__ui.event = event;
- stbte__ui.mx = x;
- stbte__ui.my = y;
- stbte__ui.dx = x - stbte__ui.last_mouse_x;
- stbte__ui.dy = y - stbte__ui.last_mouse_y;
- stbte__ui.last_mouse_x = x;
- stbte__ui.last_mouse_y = y;
- stbte__ui.accum_x += stbte__ui.dx;
- stbte__ui.accum_y += stbte__ui.dy;
- }
- void stbte_draw(stbte_tilemap *tm)
- {
- stbte__ui.event = STBTE__paint;
- stbte__editor_traverse(tm);
- }
- void stbte_mouse_move(stbte_tilemap *tm, int x, int y, int shifted, int scrollkey)
- {
- stbte__set_event(STBTE__mousemove, x,y);
- stbte__ui.shift = shifted;
- stbte__ui.scrollkey = scrollkey;
- stbte__do_event(tm);
- }
- void stbte_mouse_button(stbte_tilemap *tm, int x, int y, int right, int down, int shifted, int scrollkey)
- {
- static int events[2][2] = { { STBTE__leftup , STBTE__leftdown },
- { STBTE__rightup, STBTE__rightdown } };
- stbte__set_event(events[right][down], x,y);
- stbte__ui.shift = shifted;
- stbte__ui.scrollkey = scrollkey;
- stbte__do_event(tm);
- }
- void stbte_mouse_wheel(stbte_tilemap *tm, int x, int y, int vscroll)
- {
- // not implemented yet -- need different way of hittesting
- }
- void stbte_action(stbte_tilemap *tm, enum stbte_action act)
- {
- switch (act) {
- case STBTE_tool_select: stbte__ui.tool = STBTE__tool_select; break;
- case STBTE_tool_brush: stbte__ui.tool = STBTE__tool_brush; break;
- case STBTE_tool_erase: stbte__ui.tool = STBTE__tool_erase; break;
- case STBTE_tool_rectangle: stbte__ui.tool = STBTE__tool_rect; break;
- case STBTE_tool_eyedropper: stbte__ui.tool = STBTE__tool_eyedrop; break;
- case STBTE_tool_link: stbte__ui.tool = STBTE__tool_link; break;
- case STBTE_act_toggle_grid: stbte__ui.show_grid = (stbte__ui.show_grid+1) % 3; break;
- case STBTE_act_toggle_links: stbte__ui.show_links ^= 1; break;
- case STBTE_act_undo: stbte__undo(tm); break;
- case STBTE_act_redo: stbte__redo(tm); break;
- case STBTE_act_cut: stbte__copy_cut(tm, 1); break;
- case STBTE_act_copy: stbte__copy_cut(tm, 0); break;
- case STBTE_act_paste: stbte__start_paste(tm); break;
- case STBTE_scroll_left: tm->scroll_x -= tm->spacing_x; break;
- case STBTE_scroll_right: tm->scroll_x += tm->spacing_x; break;
- case STBTE_scroll_up: tm->scroll_y -= tm->spacing_y; break;
- case STBTE_scroll_down: tm->scroll_y += tm->spacing_y; break;
- }
- }
- void stbte_tick(stbte_tilemap *tm, float dt)
- {
- stbte__ui.event = STBTE__tick;
- stbte__ui.dt = dt;
- stbte__do_event(tm);
- stbte__ui.ms_time += (int) (dt * 1024) + 1; // make sure if time is superfast it always updates a little
- }
- void stbte_mouse_sdl(stbte_tilemap *tm, const void *sdl_event, float xs, float ys, int xo, int yo)
- {
- #ifdef _SDL_H
- SDL_Event *event = (SDL_Event *) sdl_event;
- SDL_Keymod km = SDL_GetModState();
- int shift = (km & KMOD_LCTRL) || (km & KMOD_RCTRL);
- int scrollkey = 0 != SDL_GetKeyboardState(NULL)[SDL_SCANCODE_SPACE];
- switch (event->type) {
- case SDL_MOUSEMOTION:
- stbte_mouse_move(tm, (int) (xs*event->motion.x+xo), (int) (ys*event->motion.y+yo), shift, scrollkey);
- break;
- case SDL_MOUSEBUTTONUP:
- 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);
- break;
- case SDL_MOUSEBUTTONDOWN:
- 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);
- break;
- case SDL_MOUSEWHEEL:
- stbte_mouse_wheel(tm, stbte__ui.mx, stbte__ui.my, event->wheel.y);
- break;
- }
- #else
- STBTE__NOTUSED(tm);
- STBTE__NOTUSED(sdl_event);
- STBTE__NOTUSED(xs);
- STBTE__NOTUSED(ys);
- STBTE__NOTUSED(xo);
- STBTE__NOTUSED(yo);
- #endif
- }
- #endif // STB_TILEMAP_EDITOR_IMPLEMENTATION
- /*
- ------------------------------------------------------------------------------
- This software is available under 2 licenses -- choose whichever you prefer.
- ------------------------------------------------------------------------------
- ALTERNATIVE A - MIT License
- Copyright (c) 2017 Sean Barrett
- Permission is hereby granted, free of charge, to any person obtaining a copy of
- this software and associated documentation files (the "Software"), to deal in
- the Software without restriction, including without limitation the rights to
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
- of the Software, and to permit persons to whom the Software is furnished to do
- so, subject to the following conditions:
- The above copyright notice and this permission notice shall be included in all
- copies or substantial portions of the Software.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- SOFTWARE.
- ------------------------------------------------------------------------------
- ALTERNATIVE B - Public Domain (www.unlicense.org)
- This is free and unencumbered software released into the public domain.
- Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
- software, either in source code form or as a compiled binary, for any purpose,
- commercial or non-commercial, and by any means.
- In jurisdictions that recognize copyright laws, the author or authors of this
- software dedicate any and all copyright interest in the software to the public
- domain. We make this dedication for the benefit of the public at large and to
- the detriment of our heirs and successors. We intend this dedication to be an
- overt act of relinquishment in perpetuity of all present and future rights to
- this software under copyright law.
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
- ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- ------------------------------------------------------------------------------
- */
|