Compare commits

...

80 Commits

Author SHA1 Message Date
Saveliy Skresanov
6ce8e10adb Add a limit on the number of SPARK, LFLARE and FLARE effects drawn in one frame. 2024-04-22 22:49:00 +07:00
jacob1
47384c5572
ICE contains arbitrary elements in ctype as well and should track this in saves 2024-04-21 00:11:55 -04:00
jacob1
de345a85a1
Fix "missing custom elements" warnings when loading saves with RSST/RSSS; mark SNOW as carrying ctype as well 2024-04-19 23:31:20 -04:00
Tamás Bálint Misius
6de252eb34
Check starcatcher credentials early
It's infuriating when the entire workflow fails at the publish stage because Discord pretends to have markdown.
2024-04-14 20:36:05 +02:00
Tamás Bálint Misius
b39f3c7f55
Disable --backend=vs build
Someone please give microsoft engineers a lecture on how to use computers.
2024-04-14 20:34:34 +02:00
Tamás Bálint Misius
58c0ab4747
Fix more &vec[0] problems 2024-04-13 22:32:43 +02:00
Tamás Bálint Misius
73daf67c34
Add elem.PROP_BLACK back for compat
Of course it still does nothing.
2024-04-13 19:08:19 +02:00
Simon Robertshaw
3edb8c4233 Clamp window position/size to graphics surface 2024-04-12 18:14:19 +01:00
Saveliy Skresanov
351dc6ec87 Make dropdowns near the top of the screen go down. 2024-04-12 23:55:34 +07:00
Saveliy Skresanov
0cfb91ce86 Add day/week/month/year selector in the search. 2024-04-12 22:50:55 +07:00
Saveliy Skresanov
efeac4fd8a Make "being transparent to photons" a property PROP_PHOTPASS. 2024-04-10 21:48:51 +07:00
Simon Robertshaw
36619df4f4 Revert "Remove Renderer field from GraphicsFunContext"
This reverts commit 9f02999947 & 2c55a8a9d9
2024-04-07 16:31:10 +01:00
Simon Robertshaw
9f02999947 Remove Renderer field from GraphicsFunContext
- Only decorationsEnable & blackDecorations were ever read, graphics update functions shouldn't really be aware of the renderer.
 - `sim` should ideally not be there either, but `luaGraphicsWrapper` has more going on to sort out
 - Re-format previous commit with tabs instead of spaces
2024-04-07 10:00:08 +01:00
Simon Robertshaw
2c55a8a9d9 Remove Renderer field from GraphicsFunContext
- Only decorationsEnable & blackDecorations were ever read, graphics update functions shouldn't really be aware of the renderer.
 - `sim` should ideally not be there either, but `luaGraphicsWrapper` has more going on to sort out
2024-04-07 00:17:48 +01:00
Saveliy Skresanov
1a0eb73ea0 Use create_part in resist reactions, and set CarriesCtypeIn for RSSS. 2024-04-04 22:20:14 +07:00
Saveliy Skresanov
f8873debc6 Fix RSST/RSSS not respecting the CarriesCtypeIn field. Reset GEL's tmp field when it turns into RSST. 2024-04-04 22:10:58 +07:00
Tamás Bálint Misius
51f714de0f
Stop scrolling in ScrollPanels on mousedown
The goal was to let finger flicks that didn't qualify as panning commands cancel momentum scrolling. The final effect is that any click does, which is fine.
2024-04-03 13:40:20 +02:00
jacob1
e371d6345b
Update build numbers (v98.2.365) 2024-04-01 19:54:39 -04:00
jacob1
e55fc8703a
The squirrel infestation problem is mostly resolved
This reverts commits 5e8a28b946, e8c24e7e23, e8c24e7e23
2024-04-01 19:45:14 -04:00
jacob1
40e2e4a62a
Fix text cutoff issues in save title and search error message
Save title can now overlap with the vote buttons again. It doesn't look great, but reorganizing the preview view is a task for a later day. Save titles this long are rare.
2024-04-01 12:57:19 -04:00
jacob1
e9fdb254af
Update version numbers 2024-03-31 19:48:12 -04:00
jacob1
5e8a28b946
Option to set the max amount of sounds that will play at once, between 0-999 2024-03-31 19:42:28 -04:00
jacob1
a53595ce68
coolcats.patch (patch courtesy of Simon) 2024-03-31 11:20:08 -04:00
catsoften
e8c24e7e23
Add squir... sound. Add sound 2024-03-30 22:33:13 -04:00
jacob1
7edc413cdc
Update build number 2024-03-26 20:53:34 -04:00
Tamás Bálint Misius
53b9b0e286
Fix gravity zones not being drawn in some cases
Namely, gravity zones would be rendered when the wrong wall (i.e. not Gravity wall) was selected if custom tools were present in the SC_WALL menu section. This was because their rendering was tied to a tool index into the SC_WALL menu section, rather than to a tool identifier.
2024-03-25 20:54:45 +01:00
Tamás Bálint Misius
99cd354a16
Fix dialuges looping infinitely in some cases
Namely, when no completion callback is specified, they use themselves, of all things, as their completion callback, because lua_gettop returns 0 and somehow Lua is ok with that stack index and thinks it refers to the function being executed.
2024-03-25 20:49:00 +01:00
Tamás Bálint Misius
d56e8387cf
Fix crash upon selecting a stamp too quickly
SaveButton relies on the SaveFile/SaveInfo passed to it at construction being alive until its destruction, or at least while it's being ticked (while its Tick is being called). While SaveButtons are owned by views, SaveFiles/SaveInfos associated with them are owned by models, so models need to immediately notify views (their observers) of changes made to SaveFiles/SaveInfos, or otherwise prevent views (and thus SaveButtons) from accessing stale SaveFile/SaveInfo pointers.

The online save browser model (SearchModel) correctly notifies its observer (SearchView) about changes to its list of SaveInfos. The local save browser (FileBrowserActivity) is not MVC and simply either fully exits when its SaveFile list changes, or it cleans up its SaveButtons beforehand. The stamp browser model (LocalBrowerModel), however, would make changes to its SaveFile list without notifying its observer (LocalBrowserView) when selecting a stamp, so the latter might tick its SaveButtons after SaveFiles associated with them had already been cleaned up. This commit adds the missing notification.

The crash would manifest when the SaveFile associated with a stamp was accessed for the GameSave it owned to be sent off for rendering with ThumbnailRendererTask.
2024-03-24 12:48:15 +01:00
Tamás Bálint Misius
c1c1daa9e5
Allow exporting Lua symbols
Which optionally enables loading Lua shared modules from within even static TPT. Not that anyone actually needs this.

This currently can't work on Windows because DLLs there import symbols by [module name, symbol name] rather than just symbol name. One could in theory export Lua symbols from TPT (I don't know the exact MSVC hack this would require, .def files?) and place a lua51.dll next to the executable that just re-exports them, see https://learn.microsoft.com/en-us/cpp/build/reference/exports?view=msvc-170 , but I've yet to try this.
2024-03-24 08:37:38 +01:00
Tamás Bálint Misius
bfdfebc1d5
Remove unused MD5 code 2024-03-24 08:27:06 +01:00
jacob1
502df57cae
Fix local saves being sorted Z-A instead of A-Z 2024-03-23 23:41:41 -04:00
jacob1
a0ba5f5398
Fix some label cutoff and component overlap bugs
Profile Viewer: a few "Not Provided" labels were cutoff. The score labels overlapped with the scrollbar and prevented clicking on it.
Save Preview: Views label cutoff on saves with over 10M views (aka only id:2198). authorDate label cutoff on saves with really long usernames. This one I "fixed" by making it overlap again like it used to. It only affects a few 2nd and 3rd page saves.
Options UI: Fix ambient air temp label overlapping with textbox. Fix all checkbox secondary desc labels overlapping with checkbox itself. I "fixed" this by adding the checkbox after the label so that clicks take priority, rather than actually fixing the overlap.
2024-03-23 17:26:34 -04:00
jacob1
178519dbb0
Call ren->clearScreen() in renderer if save is invalid, fixes pixel garbage appearing in final thumbnail 2024-03-23 16:10:46 -04:00
Tamás Bálint Misius
bb471e63e1
Fix panels forwarding clicks from anywhere
Very similar to ab28f93753. Broken by 69e0a8b0aa where I added an extra MouseDownInside check to the OnMouseDown (used to be OnMouseClick) of every component except that of sliders AND apparently panels, great.
2024-03-18 18:05:28 +01:00
Tamás Bálint Misius
4b866c409a
Move unlisted stamps to the back
Rather than to the front. It's nice to immediately see stamps that you haven't seen in years but it's even nicer if your most recently used stamps stay easily accessible.
2024-03-18 12:25:14 +01:00
jacob1
3fb356e2a9
Fix renderer crashing due to SimulationData not being instantiated early enough 2024-03-17 23:05:31 -04:00
jacob1
8452d96bf6
Fix update prompt showing up when no update is available if logged out and using the beta channel 2024-03-17 18:16:53 -04:00
jacob1
eef8943a3c
Fix large screen prompt not showing on first run
desktopWidth/desktopHeight were initialized only at the end of SDLOpen, after we checked if larger scales were supported. Need to initialize the window first, then recreate the window after if the scale can be larger. The window won't be centered, but otherwise it works.
2024-03-17 18:15:54 -04:00
jacob1
5160f4ad33
Update version numbers and README 2024-03-17 16:29:14 -04:00
jacob1
7870ec56a3
Limit saves with ETRD with .tmp or .tmp2 set to 98.0 2024-03-17 16:13:56 -04:00
jacob1
16f50b808d
Add limit onto various textboxes, now that textbox width no longer artificially limits it
Save Name is limited to 50 characters by the db column, so stop it there
Ambient Air Temp glitches out when it's too long, so limit to 9
Limit profile location to 40 to prevent spam / abuse
Limit tags to 16 because that's the max the server accepts
2024-03-17 15:14:22 -04:00
jacob1
aa8ee76fbb
Saves with RSST and RSSS are now restricted to 98.0 2024-03-17 14:32:28 -04:00
jacob1
228d559ccd
Replace b_strip with strip in github actions script 2024-03-15 22:42:27 -04:00
jacob1
758d34c4eb
Snapshot 361 2024-03-15 22:12:26 -04:00
jacob1
596e6cece1
Add DECOSPACE_ constants 2024-03-13 21:22:12 -04:00
jacob1
3a3a8c1cc3
Fix save history button not working except on your own saves 2024-03-06 23:11:00 -05:00
Tamás Bálint Misius
f47d0a9828
Make emscripten snapshots use starcatcher as the backend 2024-03-06 22:06:19 +01:00
Tamás Bálint Misius
7cd684ccfe
Unhardcode powdertoy.co.uk from most places
Namely, everywhere except in the network part (first line) of the intro text, since that should be customized more extensively by a client+server developer.
2024-03-06 22:03:51 +01:00
Tamás Bálint Misius
82bcb0ef9e
Fix element buttons missing from element search
Visible since c3cd4f1691, which made element search scrollable.

The last row of buttons was only shown if it had exactly as many buttons as many fit.
2024-03-05 16:10:07 +01:00
Tamás Bálint Misius
c85ebe4a0a
Fix tooltips in element search
Visible since c3cd4f1691, which made element search scrollable.

They would behave as if the scroll panel was never actually scrolled, i.e. tooltips might be shown that belonged to buttons far outside the visible part of the panel's inner area. This was because Panel invoked the OnMouseHover of its children wrong, without taking scrolling into account.
2024-03-05 16:09:34 +01:00
Tamás Bálint Misius
ab28f93753
Fix sliders accepting clicks from anywhere
Broken by 69e0a8b0aa where I added an extra MouseDownInside check to the OnMouseDown (used to be OnMouseClick) of every component except that of sliders.
2024-03-03 19:35:12 +01:00
jacob1
a7e71db9a0
Fix mingw github builds 2024-03-01 22:31:32 -05:00
jacob1
d9172faa17
Snapshot 360 2024-03-01 22:15:38 -05:00
Tamás Bálint Misius
ef308c1e48
Fix crash if tpt.installScriptManager fails
In which case the request completion handler code neglected to reset the unique_ptr holding the request.
2024-02-29 19:04:02 +01:00
Tamás Bálint Misius
79f45eb096
Use the recommended ghactions action to set up msys2 2024-02-24 22:26:53 +01:00
jacob1
0310ac08a8
Fix potential crash when sparking ETRD 2024-02-23 10:20:39 -05:00
jacob1
ea7cd41975
compat.lua: Fix tpt.brushx, tpt.brushy, and tpt.watertest 2024-02-19 22:27:19 -05:00
jacob1
f4b836deb1
draw air before "beforesimdraw" event is fired, so that graphics work in velocity / pressure display modes 2024-02-09 23:37:12 -05:00
jacob1
a250894a12
update error message when starting font editor with no arguments 2024-02-09 23:37:11 -05:00
Tamás Bálint Misius
c3cd4f1691
Make element search scrollable
But this is very buggy, ToolButtons don't lose their hover state when they get scrolled out from under the cursor and the scroll bar ignores clicks if they land on a component under it.
2024-02-06 14:56:53 +01:00
Tamás Bálint Misius
e6e36a6b7c
Fix OnMouseEnter/Leave inside Panels
Also remove OnMouseMovedInside and the dx and dy parameters of OnMouseMoved because nothing used these for anything.
2024-02-06 14:56:53 +01:00
Tamás Bálint Misius
588fe293ec
Add panning to ScrollPanel when TouchUI is enabled 2024-02-06 14:28:59 +01:00
Tamás Bálint Misius
69e0a8b0aa
Clean up OnMouseClick/Unclick madness
This is a prerequisite for making ScrollPanel work nicely on touch screens.

Engine used the terms MouseClick and MouseUnclick to refer to events that are traditionally called MouseDown and MouseUp, this was fixed with simple renaming.

Component and friends similarly used the terms MouseClick and MouseUnclick to refer to events that are traditionally called MouseDown and MouseUp and, succumbing to their own confusing terminology, also implemented behaviours associated with both the actual events MouseDown and MouseClick in code that was responsible for handling only the actual event MouseDown (i.e. what they called MouseClick).

This had been overlooked for a long time because nobody cares that a checkbox changes state when the mouse button is pressed on it rather than when it is released.

The fix is to migrate many pieces of code that run in response to MouseDown events, such as checkbox state change code, to MouseClick events, and to redefine a MouseClick to mean a sequence of MouseDown and MouseUp inside the component, rather than just a MouseDown. This is complicated by the fact that MouseClick events report mouse coordinates relative to the top left corner of the component, while MouseDown events report them relative to the top left corner of the container of the component.

Other pieces of code that make sense to be run in response to MouseDown events, such as label selection code, were left alone.
2024-02-06 13:25:53 +01:00
Tamás Bálint Misius
ae07c55f4d
Add some sign api constants 2024-01-27 19:19:56 +01:00
Tamás Bálint Misius
09ae62ac9e
Make sure vs-env.sh actually found vs
Because of course vcvarsall.bat doesn't bother to set a nonzero exit code even if it fails.
2024-01-27 18:53:17 +01:00
Tamás Bálint Misius
622df75c29
Update some more github-maintained actions
No idea why these didn't show up before I did the previous commit.

> Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: actions/upload-artifact@v3. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/.
2024-01-24 19:23:53 +01:00
Tamás Bálint Misius
cd1855fbdd
Update some github-maintained actions
> Node.js 16 actions are deprecated. Please update the following actions to use Node.js 20: actions/checkout@v3, actions/setup-python@v4. For more information see: https://github.blog/changelog/2023-09-22-github-actions-transitioning-from-node-16-to-node-20/.
2024-01-24 19:00:51 +01:00
Tamás Bálint Misius
7a10847780
Eliminate polymorphism from the CommandInterface hierarchy
This is similar to what I did to Gravity in 9068920de3. The idea is that we can choose between the implementations at compile time.
2024-01-24 18:49:34 +01:00
Tamás Bálint Misius
d87130bd66
Remove TPTScriptInterface
It was an unnecessary level in the CommandInterface hierarchy.
2024-01-24 18:09:29 +01:00
Tamás Bálint Misius
1cb8f0378f
Organize Lua API into multiple TUs 2024-01-24 11:28:27 +01:00
Tamás Bálint Misius
2a43e8aef0
Fix a deprecation warning about std::result_of_t
We still have one about std::char_traits<unsigned char> (indirectly through std::basic_string_view) though, but our lord and savouir mniip said we can migrate off of it once we're c++20.
2024-01-21 19:39:47 +01:00
Tamás Bálint Misius
3f503bcb1c
Disable custom executable manifest in vs ghactions workflows
See 1ff6a2d0ae for the original problem. The fix in that commit was a bad idea, the patch got stale and broke the previous commit.
2024-01-21 14:17:32 +01:00
Tamás Bálint Misius
a637a619c9
Disable exe icons in mingw ghactions builds
So 1ef0c1a3e0 didn't help at all, great. Either I'm terrible enough at C++ to get the same thing wrong twice, or this is a mingw bug, in which case I'm not willing to waste time trying to figure it out.
2024-01-21 13:49:26 +01:00
Tamás Bálint Misius
5839657806
Snapshot 359 2024-01-21 12:59:18 +01:00
Tamás Bálint Misius
1ef0c1a3e0
Rewrite MakeIco
There is some cursed memory problem that only ever manifests in ghactions workflows and for cursed people. I'm neither so I just rewrote the whole thing from scratch, slightly better than last time.
2024-01-21 12:57:36 +01:00
Tamás Bálint Misius
452b553350
Fix various warnings that had piled up 2024-01-21 12:46:04 +01:00
Tamás Bálint Misius
8e6faddd2f
Sanitize the Lua API
Mostly in terms of consistency of spelling (o/ou, z/s, upper/lowercase), placement (which table a function/variable is in), and getter/setter functionality (a ton of settings weren't possible to query). All of this is done, or at least is intended to be done, in a backwards-compatible manner; code that worked without errors in 97.0 should work correctly from now on also.

Also punt off duplicated and deprecated features to eventcompat.lua, renamed to just compat.lua, where they are more concisely implemented and more maintainable than on the C++ side.

Note that this means that functionality added since 97.0 is not necessarily preserved, and also that code that worked with errors may or may not keep working with errors, and it highly likely will not produce the same errors. In the future, errors coming from compat.lua should be attempted to be resolved first and foremost by migrating user code to new APIs.

List of notable, included, but not entirely relevant changes, which should probably have been done in separate commits:

 - add an enum for simulation deco spaces, there still are a few things that need such enums though
 - move clamping to Brush::SetRadius, meaning that nothing can set bogus brush sizes from now on, not even Lua
 - have LuaLuna install constructor functions in the interface table, rather than pollute _G with equivalent callable tables

The old APIs now work in accordance with existing documentation on the Wiki, though they retain weird unintended behaviour, such as accepting fewer arguments than documented. I also intend to give new APIs a thorough look later, and possibly organize C++-side code into separate TUs based on which API table it implements.

A list list of API changes follows. "status" is to be interpreted thus:

 - added: new API, the "related" API may be the limited functionality deprecated equivalent the new one is meant to extend
 - deprecated: retained for compatibility (see above), implemented in compat.lua via the "related" API, possibly meant to be removed from existing documentation, or at least very visibly marked deprecated
 - aliases: retained for compatibility (see above), implemented in compat.lua as an alias to the "related" API, possibly meant to be removed from existing documentation, or at least very visibly marked deprecated
 - renamed: not retained for compatibility because it was added after 97.0, the replacement is the "related" API
 - removed: no replacement available, see "note" for reason
 - unchanged: unchanged but noteworthy

 name                      | status     | related                 | note
---------------------------|------------|-------------------------|--------------
 Button                    | deprecated | ui.button               |
 Checkbox                  | deprecated | ui.checkbox             |
 Label                     | deprecated | ui.label                |
 ProgressBar               | deprecated | ui.progressBar          |
 Slider                    | deprecated | ui.slider               |
 Textbox                   | deprecated | ui.textbox              |
 Window                    | deprecated | ui.window               |
 bz2.COMPRESS_LIMIT        | added      | bz2.compressLimit       | identical
 bz2.COMPRESS_NOMEM        | added      | bz2.compressNomem       | identical
 bz2.DECOMPRESS_BAD        | added      | bz2.decompressBad       | identical
 bz2.DECOMPRESS_EOF        | added      | bz2.decompressEof       | identical
 bz2.DECOMPRESS_LIMIT      | added      | bz2.decompressLimit     | identical
 bz2.DECOMPRESS_NOMEM      | added      | bz2.decompressNomem     | identical
 bz2.DECOMPRESS_TYPE       | added      | bz2.decompressType      | identical
 bz2.compressLimit         | deprecated | bz2.COMPRESS_NOMEM      |
 bz2.compressNomem         | deprecated | bz2.COMPRESS_LIMIT      |
 bz2.compressOk            | removed    |                         | bz2.compress never actually returned this
 bz2.decompressBad         | deprecated | bz2.DECOMPRESS_NOMEM    |
 bz2.decompressEof         | deprecated | bz2.DECOMPRESS_LIMIT    |
 bz2.decompressLimit       | deprecated | bz2.DECOMPRESS_TYPE     |
 bz2.decompressNomem       | deprecated | bz2.DECOMPRESS_BAD      |
 bz2.decompressOk          | removed    |                         | bz2.decompress never actually returned this
 bz2.decompressType        | deprecated | bz2.DECOMPRESS_EOF      |
 elem.FLAG_MOVABLE         | deprecated | sim.FLAG_MOVABLE        |
 elem.FLAG_PHOTDECO        | deprecated | sim.FLAG_PHOTDECO       |
 elem.FLAG_SKIPMOVE        | deprecated | sim.FLAG_SKIPMOVE       |
 elem.FLAG_STAGNANT        | deprecated | sim.FLAG_STAGNANT       |
 elem.PROP_DRAWONCTYPE     | deprecated | 0                       |
 elem.ST_GAS               | deprecated | 0                       |
 elem.ST_LIQUID            | deprecated | 0                       |
 elem.ST_NONE              | deprecated | 0                       |
 elem.ST_SOLID             | deprecated | 0                       |
 elem.getByName            | added      | tpt.element             | only converts names to IDs
 evt.AFTERSIM              | added      | evt.aftersim            | identical
 evt.AFTERSIMDRAW          | added      | evt.aftersimdraw        | identical
 evt.BEFORESIM             | added      | evt.beforesim           | identical
 evt.BEFORESIMDRAW         | added      | evt.beforesimdraw       | identical
 evt.BLUR                  | added      | evt.blur                | identical
 evt.CLOSE                 | added      | evt.close               | identical
 evt.KEYPRESS              | added      | evt.keypress            | identical
 evt.KEYRELEASE            | added      | evt.keyrelease          | identical
 evt.MOUSEDOWN             | added      | evt.mousedown           | identical
 evt.MOUSEMOVE             | added      | evt.mousemove           | identical
 evt.MOUSEUP               | added      | evt.mouseup             | identical
 evt.MOUSEWHEEL            | added      | evt.mousewheel          | identical
 evt.TEXTEDITING           | added      | evt.textediting         | identical
 evt.TEXTINPUT             | added      | evt.textinput           | identical
 evt.TICK                  | added      | evt.tick                | identical
 evt.aftersim              | deprecated | evt.AFTERSIM            |
 evt.aftersimdraw          | deprecated | evt.AFTERSIMDRAW        |
 evt.beforesim             | deprecated | evt.BEFORESIM           |
 evt.beforesimdraw         | deprecated | evt.BEFORESIMDRAW       |
 evt.blur                  | deprecated | evt.BLUR                |
 evt.close                 | deprecated | evt.CLOSE               |
 evt.getModifiers          | added      | evt.getmodifiers        | identical
 evt.getmodifiers          | deprecated | evt.getModifiers        |
 evt.keypress              | deprecated | evt.KEYPRESS            |
 evt.keyrelease            | deprecated | evt.KEYRELEASE          |
 evt.mousedown             | deprecated | evt.MOUSEDOWN           |
 evt.mousemove             | deprecated | evt.MOUSEMOVE           |
 evt.mouseup               | deprecated | evt.MOUSEUP             |
 evt.mousewheel            | deprecated | evt.MOUSEWHEEL          |
 evt.textediting           | deprecated | evt.TEXTEDITING         |
 evt.textinput             | deprecated | evt.TEXTINPUT           |
 evt.tick                  | deprecated | evt.TICK                |
 ren.FIRE_SPARK            | added      |                         | was missing
 ren.colourMode            | deprecated | ren.colorMode           |
 ren.debugHUD              | deprecated | ren.debugHud            |
 ren.decorations           | added      | tpt.decorations_enable  | works with booleans
 ren.fireSize              | added      | tpt.setfire             | also a getter, takes intensity only
 ren.hud                   | added      | tpt.hud                 | works with booleans
 ren.useDisplayPreset      | added      | tpt.display_mode        | identical
 sim.AIR_NOUPDATE          | added      | sim.AIR_NO_UPDATE       | identical
 sim.AIR_NO_UPDATE         | renamed    | sim.AIR_NOUPDATE        |
 sim.AIR_PRESSUREOFF       | added      | sim.AIR_PRESSURE_OFF    | identical
 sim.AIR_PRESSURE_OFF      | renamed    | sim.AIR_PRESSUREOFF     |
 sim.AIR_VELOCITYOFF       | added      | sim.AIR_VELOCITY_OFF    | identical
 sim.AIR_VELOCITY_OFF      | renamed    | sim.AIR_VELOCITYOFF     |
 sim.BRUSH_CIRCLE          | added      | sim.CIRCLE_BRUSH        | identical
 sim.BRUSH_NUM             | renamed    | sim.NUM_DEFAULTBRUSHES  |
 sim.BRUSH_SQUARE          | added      | sim.SQUARE_BRUSH        | identical
 sim.BRUSH_TRIANGLE        | added      | sim.TRI_BRUSH           | identical
 sim.CIRCLE_BRUSH          | renamed    | sim.BRUSH_CIRCLE        |
 sim.FLAG_MOVABLE          | added      | elem.FLAG_MOVABLE       | identical
 sim.FLAG_PHOTDECO         | added      | elem.FLAG_PHOTDECO      | identical
 sim.FLAG_SKIPMOVE         | added      | elem.FLAG_SKIPMOVE      | identical
 sim.FLAG_STAGNANT         | added      | elem.FLAG_STAGNANT      | identical
 sim.MAX_PARTS             | added      | sim.NPART               | identical
 sim.NPART                 | renamed    | sim.MAX_PARTS           |
 sim.NUM_AIRMODES          | added      | sim.NUM_AIR_MODES       | identical
 sim.NUM_AIR_MODES         | renamed    | sim.NUM_AIRMODES        |
 sim.NUM_BRUSHES           | added      |                         | specifies the range of valid ui.brushID inputs
 sim.NUM_DEFAULTBRUSHES    | added      | sim.BRUSH_NUM           | identical
 sim.NUM_EDGEMODES         | added      | sim.NUM_EDGE_MODES      | identical
 sim.NUM_EDGE_MODES        | renamed    | sim.NUM_EDGEMODES       |
 sim.NUM_GRAVMODES         | added      | sim.NUM_GRAV_MODES      | identical
 sim.NUM_GRAV_MODES        | renamed    | sim.NUM_GRAVMODES       |
 sim.NUM_PARTS             | deprecated | sim.partCount           |
 sim.NUM_WALLS             | added      |                         | specifies the range of valid sim.wallMap inputs
 sim.SQUARE_BRUSH          | renamed    | sim.BRUSH_SQUARE        |
 sim.TRI_BRUSH             | renamed    | sim.BRUSH_TRIANGLE      |
 sim.ambientHeatSim        | added      | tpt.ambient_heat        | works with booleans
 sim.canMove               | added      | sim.can_move            | identical
 sim.can_move              | deprecated | sim.canMove             |
 sim.decoColour            | deprecated | sim.decoColor           |
 sim.decoSpace             | added      | tpt.decoSpace           | identical, but a function
 sim.elecMap               | added      | tpt.set_elecmap         | also a getter
 sim.ensureDeterminism     | unchanged  |                         | undocumented
 sim.fanVelocityX          | added      | tpt.set_wallmap         | also a getter, sets fan velocity separately
 sim.fanVelocityY          | added      | tpt.set_wallmap         | also a getter, sets fan velocity separately
 sim.frameRender           | added      | sim.framerender         | identical
 sim.framerender           | deprecated | sim.frameRender         |
 sim.golSpeedRatio         | added      | sim.gspeed              | identical
 sim.gravMap               | deprecated | various                 |
 sim.gravityField          | added      | sim.graMap              | gravity simulation output
 sim.gravityMass           | added      | sim.graMap              | also a getter, gravity simulation input
 sim.gspeed                | deprecated | sim.golSpeedRatio       |
 sim.hash                  | unchanged  |                         | undocumented
 sim.heatSim               | added      | tpt.heat                | works with booleans
 sim.neighbours            | deprecated | sim.neighbors           |
 sim.newtonianGravity      | added      | tpt.newtonian_gravity   | works with booleans
 sim.partCount             | added      | tpt.NUM_PARTS           | identical, but a function
 sim.partNeighbours        | deprecated | see sim.partNeighbors   |
 sim.paused                | added      | tpt.set_pause           | works with booleans
 sim.randomSeed            | added      | sim.randomseed          | identical, undocumented
 sim.randomseed            | renamed    | sim.randomSeed          | undocumented
 sim.resetGravityField     | added      | tpt.reset_gravity_field | identical
 sim.resetSpark            | added      | tpt.reset_spark         | identical
 sim.resetVelocity         | added      | tpt.reset_velocity      | identical
 sim.wallMap               | added      | tpt.set_wallmap         | also a getter, doesn't set fan velocity
 sim.waterEqualisation     | deprecated | sim.waterEqualization   |
 sim.waterEqualization     | unchanged  |                         | still works with ints, not worth the trouble
 socket.getTime            | added      | socket.gettime          | identical
 socket.gettime            | deprecated | socket.getTime          |
 tpt.active_menu           | deprecated | ui.activeMenu           |
 tpt.ambient_heat          | deprecated | sim.ambientHeatSim      |
 tpt.brushID               | deprecated | ui.brushID              |
 tpt.brushx                | deprecated | ui.brushRadius          |
 tpt.brushy                | deprecated | ui.brushRadius          |
 tpt.create                | deprecated | various                 |
 tpt.debug                 | added      | tpt.setdebug            | identical
 tpt.decoSpace             | deprecated | sim.decoSpace           |
 tpt.decorations_enable    | deprecated | ren.decorations         |
 tpt.delete                | deprecated | various                 |
 tpt.display_mode          | deprecated | ren.useDisplayPreset    |
 tpt.drawCap               | added      | tpt.setdrawcap          | identical
 tpt.drawline              | deprecated | gfx.drawLine            |
 tpt.drawpixel             | deprecated | gfx.drawPixel           |
 tpt.drawrect              | deprecated | gfx.drawRect            |
 tpt.drawtext              | deprecated | gfx.drawText            |
 tpt.el                    | deprecated | various                 |
 tpt.element               | deprecated | various                 |
 tpt.element_func          | deprecated | elem.property           |
 tpt.eltransition          | deprecated | various                 |
 tpt.fillrect              | deprecated | gfx.fillRect            |
 tpt.fpsCap                | added      | tpt.setfpscap           | identical
 tpt.getPartIndex          | deprecated | various                 |
 tpt.getUserName           | added      | tpt.get_name            | identical
 tpt.get_clipboard         | deprecated | plat.clipboardCopy      |
 tpt.get_elecmap           | deprecated | sim.elecMap             |
 tpt.get_name              | deprecated | tpt.getUserName         |
 tpt.get_numOfParts        | deprecated | sim.partCount           |
 tpt.get_property          | deprecated | sim.partProperty        |
 tpt.get_wallmap           | deprecated | sim.wallMap             |
 tpt.graphics_func         | deprecated | elem.property           |
 tpt.heat                  | deprecated | sim.heatSim             |
 tpt.hud                   | deprecated | ren.hud                 |
 tpt.menu_enabled          | deprecated | ui.menuEnabled          |
 tpt.mousex                | deprecated | ui.mousePosition        |
 tpt.mousey                | deprecated | ui.mousePosition        |
 tpt.newtonian_gravity     | deprecated | sim.newtonianGravity    |
 tpt.next_getPartIndex     | deprecated | various                 |
 tpt.num_menus             | deprecated | ui.numMenus             |
 tpt.parts                 | deprecated | various                 |
 tpt.perfectCircleBrush    | deprecated | ui.perfectCircleBrush   |
 tpt.reset_gravity_field   | deprecated | sim.resetGravityField   |
 tpt.reset_spark           | deprecated | sim.resetSpark          |
 tpt.reset_velocity        | deprecated | sim.resetVelocity       |
 tpt.selecteda             | deprecated | ui.activeTool           |
 tpt.selectedl             | deprecated | ui.activeTool           |
 tpt.selectedr             | deprecated | ui.activeTool           |
 tpt.selectedreplace       | deprecated | ui.activeTool           |
 tpt.set_clipboard         | deprecated | plat.clipboardPaste     |
 tpt.set_console           | deprecated | ui.console              |
 tpt.set_elecmap           | deprecated | sim.elecMap             |
 tpt.set_gravity           | deprecated | sim.gravityMass         |
 tpt.set_pause             | deprecated | sim.paused              |
 tpt.set_pressure          | deprecated | sim.pressure            |
 tpt.set_property          | deprecated | sim.partProperty        |
 tpt.set_wallmap           | deprecated | sim.wallMap             |
 tpt.setdebug              | deprecated | tpt.debug               |
 tpt.setdrawcap            | deprecated | tpt.drawCap             |
 tpt.setfire               | deprecated | ren.fireSize            |
 tpt.setfpscap             | deprecated | tpt.fpsCap              |
 tpt.setwindowsize         | deprecated | ui.windowSize           |
 tpt.start_getPartIndex    | deprecated | various                 |
 tpt.textwidth             | deprecated | gfx.textSize            |
 tpt.toggle_pause          | deprecated | sim.paused              |
 tpt.watertest             | deprecated | sim.waterEqualization   |
 ui.MOUSEUP_BLUR           | added      | ui.MOUSE_UP_BLUR        | identical
 ui.MOUSEUP_DRAWEND        | added      | ui.MOUSE_UP_DRAW_END    | identical
 ui.MOUSEUP_NORMAL         | added      | ui.MOUSE_UP_NORMAL      | identical
 ui.MOUSE_UP_BLUR          | deprecated | ui.MOUSEUP_BLUR         |
 ui.MOUSE_UP_DRAW_END      | deprecated | ui.MOUSEUP_DRAWEND      |
 ui.MOUSE_UP_NORMAL        | deprecated | ui.MOUSEUP_NORMAL       |
 ui.NUM_TOOLINDICES        | added      |                         | specifies the range of valid ui.activeTool inputs
 ui.activeMenu             | added      | tpt.active_menu         | identical
 ui.activeTool             | added      | tpt.selectedl, ...      | identical, but a function
 ui.brushID                | added      | tpt.brushID             | identical, but a function
 ui.brushRadius            | added      | tpt.brushx, tpt.brushy  | identical, but a function
 ui.button                 | added      | Slider:new              | standalone function
 ui.checkbox               | added      | Textbox:new             | standalone function
 ui.console                | added      | tpt.set_console         | works with booleans
 ui.label                  | added      | ProgressBar:new         | standalone function
 ui.menuEnabled            | added      | tpt.menu_enabled        | identical
 ui.mousePosition          | added      | tpt.mousex, tpt.mousey  | identical, but a function
 ui.numMenus               | added      | tpt.num_menus           | identical
 ui.perfectCircleBrush     | added      | tpt.perfectCircleBrush  | identical
 ui.progressBar            | added      | Window:new              | standalone function
 ui.slider                 | added      | Button:new              | standalone function
 ui.textbox                | added      | Label:new               | standalone function
 ui.window                 | added      | Checkbox:new            | standalone function
 ui.windowSize             | added      | tpt.setwindowsize       | also a getter
2024-01-21 11:30:24 +01:00
Tamás Bálint Misius
5e60b53a3b
Hopefully fix msys2 workflows
Apparently updating msys2 causes it to exit completely, great.
2024-01-20 23:35:53 +01:00
Tamás Bálint Misius
5eb5383f08
Fix Platform::ExecutableName on freebsd
Also fix release (but somehow only release) builds failing to link because execinfo is a library on freebsd.
2024-01-15 19:54:32 +01:00
Cracker1000
708d543d29
Make LDTC copy BIZR, BIZRG and BIZRG's wavelength too. (#936) 2024-01-15 07:18:16 +01:00
181 changed files with 8830 additions and 9250 deletions

36
.github/build.sh vendored
View File

@ -70,12 +70,13 @@ if [[ -z ${BSH_NO_PACKAGES-} ]]; then
;;
windows)
if [[ $BSH_BUILD_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then
pacman -Syu --noconfirm --needed mingw-w64-ucrt-x86_64-gcc
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-gcc
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{cmake,7zip} patch
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{cmake,7zip,jq} patch
else
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{pkgconf,bzip2,luajit,jsoncpp,curl,SDL2,libpng,meson,fftw}
pacman -S --noconfirm --needed mingw-w64-ucrt-x86_64-{pkgconf,bzip2,luajit,jsoncpp,curl,SDL2,libpng,meson,fftw,jq}
fi
export PKG_CONFIG=$(which pkg-config.exe)
fi
;;
linux)
@ -198,7 +199,7 @@ meson_configure+=$'\t'-Dapp_exe=$APP_EXE
meson_configure+=$'\t'-Dapp_id=$APP_ID
meson_configure+=$'\t'-Dapp_data=$APP_DATA
meson_configure+=$'\t'-Dapp_vendor=$APP_VENDOR
meson_configure+=$'\t'-Db_strip=false
meson_configure+=$'\t'-Dstrip=false
meson_configure+=$'\t'-Db_staticpic=false
meson_configure+=$'\t'-Dmod_id=$MOD_ID
case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in
@ -218,23 +219,9 @@ if [[ $PACKAGE_MODE == nolua ]]; then
fi
if [[ $PACKAGE_MODE == backendvs ]]; then
meson_configure+=$'\t'-Dbackend=vs
echo "NOTE: patching CREATEPROCESS_MANIFEST_RESOURCE_ID out of powder-res.template.rc"
echo "TODO: remove this patch once https://github.com/mesonbuild/meson/pull/12472 makes it into a release"
echo "TODO: also remove the relevant note from the building guide"
git apply <<PATCH
diff --git a/resources/powder-res.template.rc b/resources/powder-res.template.rc
index 1dc26c78..2094049f 100644
--- a/resources/powder-res.template.rc
+++ b/resources/powder-res.template.rc
@@ -7,7 +7,6 @@
IDI_ICON ICON DISCARDABLE "@ICON_EXE_ICO@"
IDI_DOC_ICON ICON DISCARDABLE "@ICON_CPS_ICO@"
-CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@WINUTF8_XML@"
VS_VERSION_INFO VERSIONINFO
FILEVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@
PATCH
# meson 1.2.3 configures vs projects that bring their own manifest, which conflicts with ours
# TODO: remove this patch once https://github.com/mesonbuild/meson/pull/12472 makes it into a release that we can use
meson_configure+=$'\t'-Dwindows_utf8cp=false
fi
if [[ $BSH_STATIC_DYNAMIC == static ]]; then
meson_configure+=$'\t'-Dstatic=prebuilt
@ -303,12 +290,19 @@ if [[ $RELEASE_TYPE == snapshot ]] && [[ $MOD_ID != 0 ]]; then
fi
if [[ $RELEASE_TYPE == snapshot ]] || [[ $MOD_ID != 0 ]]; then
meson_configure+=$'\t'-Dupdate_server=starcatcher.us/TPT
if [[ $BSH_HOST_PLATFORM == emscripten ]]; then
meson_configure+=$'\t'-Dserver=tptserv.starcatcher.us
meson_configure+=$'\t'-Dstatic_server=tptserv.starcatcher.us/Static
fi
fi
if [[ $RELEASE_TYPE != dev ]]; then
meson_configure+=$'\t'-Dignore_updates=false
fi
if [[ "$BSH_HOST_PLATFORM-$BSH_HOST_LIBC" == "windows-mingw" ]]; then
meson_configure+=$'\t'--cross-file=.github/mingw-ghactions.ini
# there is some mingw bug that only ever manifests on ghactions which makes MakeIco.exe use tons of memory and fail
# TODO: remove this hack once we figure out how to fix that
meson_configure+=$'\t'-Dwindows_icons=false
fi
if [[ $BSH_DEBUG_RELEASE-$BSH_STATIC_DYNAMIC == release-static ]]; then
meson_configure+=$'\t'-Db_lto=true

View File

@ -1,10 +0,0 @@
set -euo pipefail
IFS=$'\t\n'
echo 'C:\msys64\ucrt64\bin' >> tmp
echo 'C:\msys64\usr\bin' >> tmp
cat $GITHUB_PATH >> tmp
mv tmp $GITHUB_PATH
echo "MSYSTEM=UCRT64" >> $GITHUB_ENV
echo "PKG_CONFIG="'C:\msys64\ucrt64\bin\pkg-config.exe' >> $GITHUB_ENV

9
.github/prepare.py vendored
View File

@ -114,7 +114,7 @@ for arch, platform, libc, statdyn, bplatform, runso
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
( 'x86_64', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None,'x86_64-win-msvc-static', 'release', 10 ),
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 10 ),
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, 'backendvs', None, 'debug', 0 ), # priority = 0: backend=vs build
# ( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, 'backendvs', None, 'debug', 0 ), # priority = 0: backend=vs build
( 'x86_64', 'windows', 'msvc', 'dynamic', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'release', 10 ),
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', False, False, None, None, None, 'debug', 0 ), # priority = 0: static debug build
( 'x86', 'windows', 'msvc', 'static', 'windows', 'windows-2019', '.exe', True, True, '.pdb', None, 'i686-win-msvc-static', 'release', 10 ),
@ -173,6 +173,10 @@ for arch, platform, libc, statdyn, bplatform, runso
debug_asset_path = f'{app_name_slug}-{arch}.AppImage.dbg'
debug_asset_name = f'{app_name_slug}-{arch}.AppImage.dbg'
starcatcher_name = f'powder-{release_name}-{starcatcher}{suffix}'
msys2_bash = (bplatform == 'windows' and libc == 'mingw')
shell = 'bash'
if msys2_bash:
shell = 'msys2 {0}'
build_matrix.append({
'bsh_build_platform': bplatform, # part of the unique portion of the matrix
'bsh_host_arch': arch, # part of the unique portion of the matrix
@ -181,7 +185,7 @@ for arch, platform, libc, statdyn, bplatform, runso
'bsh_static_dynamic': statdyn, # part of the unique portion of the matrix
'bsh_debug_release': dbgrel, # part of the unique portion of the matrix
'runs_on': runson,
'force_msys2_bash': (bplatform == 'windows' and libc == 'mingw') and 'yes' or 'no',
'force_msys2_bash': msys2_bash and 'yes' or 'no',
'package_suffix': suffix,
'package_mode': mode,
'publish': publish and 'yes' or 'no',
@ -192,6 +196,7 @@ for arch, platform, libc, statdyn, bplatform, runso
'debug_asset_path': debug_asset_path,
'debug_asset_name': debug_asset_name,
'job_name': job_name,
'shell': shell,
})
if publish:
publish_matrix.append({

View File

@ -13,6 +13,8 @@ chmod 660 ~/.netrc
mountpoint=ftpmnt
mkdir $mountpoint
curlftpfs "$PUBLISH_HOSTPORT" $mountpoint -o ssl,ciphers='ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES128-SHA:DHE-RSA-AES128-GCM-SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256:TLS_AES_128_CCM_8_SHA256:TLS_AES_128_CCM_SHA256'
cp $PUBLISH_FILENAME $mountpoint/${PUBLISH_DIRECTORY:-.}/
if [[ -z ${PUBLISH_ACCESSCHECK-} ]]; then
cp $PUBLISH_FILENAME $mountpoint/${PUBLISH_DIRECTORY:-.}/
fi
fusermount -u $mountpoint
rmdir $mountpoint

3
.github/vs-env.sh vendored
View File

@ -24,5 +24,8 @@ IFS=$'\t\n'
for i in $(MSYS_NO_PATHCONV=1 cmd /c "$vs_install_dir\\VC\\Auxiliary\\Build\\vcvarsall.bat" $VS_ENV_PARAMS \& env \& exit /b); do
set +e
export "$i" 2>/dev/null
echo $i | grep ERROR
set -e
done
cl

View File

@ -31,8 +31,8 @@ jobs:
do_publish: ${{ steps.prepare.outputs.do_publish }}
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
@ -41,6 +41,13 @@ jobs:
env:
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
GITHUB_REF: ${{ github.ref }}
- if: steps.prepare.outputs.do_publish == 'yes'
run: sudo apt update && sudo apt install curlftpfs && bash -c './.github/starcatcher-publish.sh'
env:
PUBLISH_HOSTPORT: ${{ secrets.STARCATCHER_PUBLISH_HOSTPORT }}
PUBLISH_USERNAME: ${{ secrets.STARCATCHER_PUBLISH_USERNAME }}
PUBLISH_PASSWORD: ${{ secrets.STARCATCHER_PUBLISH_PASSWORD }}
PUBLISH_ACCESSCHECK: yes
- if: steps.prepare.outputs.do_release == 'yes'
id: create_release
env:
@ -54,15 +61,46 @@ jobs:
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }}
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- if: matrix.force_msys2_bash == 'yes'
run: bash -c './.github/force-msys2-bash.sh'
- uses: actions/setup-python@v4
uses: msys2/setup-msys2@v2
with:
msystem: UCRT64
update: true
path-type: strict
cache: true
# this list doesn't have to mirror the one in build.sh perfectly
# but the packages listed here get cached properly and take less time to install
install: >-
git
curl
mingw-w64-ucrt-x86_64-gcc
mingw-w64-ucrt-x86_64-pkgconf
mingw-w64-ucrt-x86_64-bzip2
mingw-w64-ucrt-x86_64-luajit
mingw-w64-ucrt-x86_64-jsoncpp
mingw-w64-ucrt-x86_64-curl
mingw-w64-ucrt-x86_64-SDL2
mingw-w64-ucrt-x86_64-libpng
mingw-w64-ucrt-x86_64-meson
mingw-w64-ucrt-x86_64-python
mingw-w64-ucrt-x86_64-python-pip
mingw-w64-ucrt-x86_64-fftw
mingw-w64-ucrt-x86_64-cmake
mingw-w64-ucrt-x86_64-7zip
mingw-w64-ucrt-x86_64-jq
patch
- run: git config --global core.autocrlf false
- uses: actions/checkout@v4
- if: matrix.force_msys2_bash != 'yes'
uses: actions/setup-python@v5
with:
python-version: '3.10'
- run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
- if: matrix.force_msys2_bash != 'yes'
run: python -m pip install meson==1.2.3 ninja # TODO: go back to using latest meson once https://github.com/mesonbuild/meson/pull/12544 is live
- if: matrix.bsh_build_platform == 'darwin'
run: brew install bash coreutils
- run: bash -c './.github/build.sh'
@ -98,12 +136,12 @@ jobs:
ASSET_PATH: build/${{ matrix.debug_asset_path }}
ASSET_NAME: ${{ matrix.debug_asset_name }}
run: bash -c './.github/upload-release-asset.sh'
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: matrix.artifact == 'yes'
with:
path: build/${{ matrix.asset_path }}
name: ${{ matrix.asset_name }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
if: matrix.artifact == 'yes' && matrix.separate_debug == 'yes'
with:
path: build/${{ matrix.debug_asset_path }}
@ -117,8 +155,8 @@ jobs:
if: needs.prepare.outputs.do_publish == 'yes'
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/download-artifact@v3
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: ${{ matrix.asset_name }}
- run: mv ${{ matrix.asset_path }} ${{ matrix.starcatcher_name }}
@ -135,7 +173,7 @@ jobs:
if: needs.prepare.outputs.do_publish == 'yes'
steps:
- run: git config --global core.autocrlf false
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- run: ./.github/starcatcher-release.sh
env:
RELEASE_NAME: ${{ needs.prepare.outputs.release_name }}

View File

@ -1,4 +1,4 @@
The Powder Toy - January 2023
The Powder Toy - April 2024
==========================
Get the latest version [from the Powder Toy website](https://powdertoy.co.uk/Download.html).
@ -108,6 +108,7 @@ Controls
| Shift + R | Horizontal mirror for selected area when pasting stamps |
| Ctrl + Shift + R | Vertical mirror for selected area when pasting stamps |
| R | Rotate selected area counterclockwise when pasting stamps |
| F11 | Toggle fullscreen |
Command Line
---------------------------------------------------------------------------

View File

@ -55,6 +55,7 @@ lldb_server=${LLDB_SERVER:-$default_lldb_server}
lldb_server_port=${LLDB_SERVER_PORT:-9998}
jdb_port=${JDB_PORT:-13456}
lldb_client=${LLDB_CLIENT:-$default_lldb_client}
meson=${MESON:-meson}
adb=${ADB:-adb}
jdb=${JDB:-jdb}
@ -113,6 +114,11 @@ Naturally, replace bagelsbagels with an appropriate password.
HELP
exit 1
fi
>&2 echo "[+] meson compiling android/$app_exe.apk"
if ! $meson compile sign-apk; then
>&2 echo "[-] failed"
return 1
fi
>&2 echo "[+] adb installing android/$app_exe.apk"
if ! $adb install android/$app_exe.apk; then
>&2 echo "[-] failed"

View File

@ -376,6 +376,22 @@ if host_platform == 'emscripten'
'-o', app_exe + '.js', # so we get a .wasm, and a .js
]
endif
if get_option('export_lua_symbols')
if is_static and lua_variant != 'none' and not project_export_dynamic
if host_platform == 'windows'
error('Lua symbols are currently impossible to export correctly on Windows')
elif c_compiler.has_link_argument('-Wl,--export-dynamic-symbol')
project_link_args += [
'-Wl,--export-dynamic-symbol=lua_*',
'-Wl,--export-dynamic-symbol=luaL_*',
'-Wl,--export-dynamic-symbol=luaopen_*',
]
else
warning('your linker does not support -Wl,--export-dynamic-symbol so Meson will be instructed to export all symbols in order to enable loading Lua shared modules, which may blow up the size of the resulting binary')
project_export_dynamic = true
endif
endif
endif
if get_option('build_powder')
powder_deps += project_deps + [

View File

@ -40,42 +40,42 @@ option(
'display_version_major',
type: 'integer',
min: 0,
value: 97,
value: 98,
description: 'Major component of the display version, should more or less map to the MINOR version in semantic versioning'
)
option(
'display_version_minor',
type: 'integer',
min: 0,
value: 0,
value: 2,
description: 'Minor component of the display version, should more or less map to the PATCH version in semantic versioning'
)
option(
'build_num',
type: 'integer',
min: 0,
value: 358,
value: 365,
description: 'Build number, should be strictly monotonously increasing across public releases'
)
option(
'upstream_version_major',
type: 'integer',
min: 0,
value: 97,
value: 98,
description: 'Major component of the upstream display version, mod owners should not change this but merge upstream changes to it'
)
option(
'upstream_version_minor',
type: 'integer',
min: 0,
value: 0,
value: 2,
description: 'Minor component of the upstream display version, mod owners should not change this but merge upstream changes to it'
)
option(
'upstream_build_num',
type: 'integer',
min: 0,
value: 358,
value: 365,
description: 'Upstream build number, mod owners should not change this but merge upstream changes to it'
)
option(
@ -293,3 +293,21 @@ option(
value: 'auto',
description: 'Show blue error screen upon unhandled signals and exceptions'
)
option(
'windows_icons',
type: 'boolean',
value: true,
description: 'Add icon resources to the executable on Windows'
)
option(
'windows_utf8cp',
type: 'boolean',
value: true,
description: 'Ask Windows nicely for UTF-8 as the codepage'
)
option(
'export_lua_symbols',
type: 'boolean',
value: false,
description: 'Export Lua symbols to enable loading of Lua shared modules'
)

View File

@ -1,67 +1,137 @@
#include <fstream>
#include <vector>
#include <cstdint>
#include <cstring>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
static void writeU32LE(uint8_t *dest, uint32_t value)
{
dest[0] = uint8_t( value & 0xFF);
dest[1] = uint8_t((value >> 8) & 0xFF);
dest[2] = uint8_t((value >> 16) & 0xFF);
dest[3] = uint8_t((value >> 24) & 0xFF);
}
static uint32_t readU32BE(const uint8_t *src)
{
return uint32_t(src[3]) |
(uint32_t(src[2]) << 8) |
(uint32_t(src[1]) << 16) |
(uint32_t(src[0]) << 24);
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
return 1;
}
auto *outputIcoPath = argv[1];
std::ofstream outputIco(outputIcoPath, std::ios::binary);
if (!outputIco)
{
return 2;
std::cerr << "usage: " << argv[0] << " OUTPUT INPUT..." << std::endl;
exit(1);
}
auto images = argc - 2;
std::vector<char> header(22 + images * 16);
auto *incondir = &header[0];
*reinterpret_cast<uint16_t *>(&incondir[0]) = 0; // reserved
*reinterpret_cast<uint16_t *>(&incondir[2]) = 1; // icon
*reinterpret_cast<uint16_t *>(&incondir[4]) = uint16_t(images);
std::vector<char> allData;
for (auto i = 0; i < images; ++i)
if (images > 255)
{
auto *inputAnyPath = argv[i + 2];
std::ifstream inputAny(inputAnyPath, std::ios::binary);
std::vector<char> data;
while (true)
{
char ch;
inputAny.read(&ch, 1);
if (inputAny.eof())
{
break;
}
if (!inputAny)
{
return 3;
}
data.push_back(ch);
}
if (*reinterpret_cast<uint64_t *>(&data[0]) != UINT64_C(0x0A1A0A0D474E5089)) // png magic
{
return 5;
}
auto width = uint8_t(data[19]);
auto height = uint8_t(data[23]);
auto *incondirentry = &header[6 + i * 16];
*reinterpret_cast<uint8_t *>(&incondirentry[0]) = width;
*reinterpret_cast<uint8_t *>(&incondirentry[1]) = height;
*reinterpret_cast<uint8_t *>(&incondirentry[2]) = 0; // no color palette
*reinterpret_cast<uint8_t *>(&incondirentry[3]) = 0; // reserved
*reinterpret_cast<uint16_t *>(&incondirentry[4]) = 1; // 1 color plane
*reinterpret_cast<uint16_t *>(&incondirentry[6]) = 32; // 32 bits per pixel
*reinterpret_cast<uint32_t *>(&incondirentry[8]) = uint32_t(data.size()); // data size
*reinterpret_cast<uint32_t *>(&incondirentry[12]) = uint32_t(header.size() + allData.size()); // data offset
allData.insert(allData.end(), data.begin(), data.end());
std::cerr << "too many images specified" << std::endl;
exit(1);
}
outputIco.write(&header[0], header.size());
outputIco.write(&allData[0], allData.size());
if (!outputIco)
std::string outputPath = argv[1];
std::ofstream output(outputPath, std::ios::binary);
auto outputFailure = [&outputPath](std::string action) {
std::cerr << "failed to " << action << " " << outputPath << ": " << strerror(errno) << std::endl;
exit(1);
};
if (!output)
{
return 4;
outputFailure("open");
}
std::vector<char> header(6 + images * 16, 0);
auto writeHeader = [&header, &output, &outputFailure]() {
output.seekp(0, std::ios_base::beg);
output.write(&header[0], header.size());
if (!output)
{
outputFailure("write");
}
};
writeHeader(); // make space for header
auto *headerU8 = reinterpret_cast<uint8_t *>(&header[0]);
headerU8[2] = 1;
headerU8[4] = images;
for (auto image = 0; image < images; ++image)
{
std::string inputPath = argv[2 + image];
std::ifstream input(inputPath, std::ios::binary);
auto inputFailure = [&inputPath](std::string action) {
std::cerr << "failed to " << action << " " << inputPath << ": " << strerror(errno) << std::endl;
exit(1);
};
auto imageFailure = [&inputPath](std::string failure) {
std::cerr << "failed to process " << inputPath << ": " << failure << std::endl;
exit(1);
};
if (!input)
{
inputFailure("open");
}
std::vector<char> buf;
input.seekg(0, std::ios_base::end);
buf.resize(input.tellg());
input.seekg(0, std::ios_base::beg);
input.read(&buf[0], buf.size());
if (!input)
{
inputFailure("read");
}
auto *bufU8 = reinterpret_cast<uint8_t *>(&buf[0]);
if (buf.size() < 0x21 ||
readU32BE(&bufU8[0]) != UINT32_C(0x89504E47) ||
readU32BE(&bufU8[4]) != UINT32_C(0x0D0A1A0A) ||
bufU8[0x18] != 8 ||
bufU8[0x19] != 6)
{
imageFailure("not a 32bpp RGBA PNG");
}
auto writeOffset = output.tellp();
output.write(&buf[0], buf.size());
if (!output)
{
outputFailure("write");
}
auto width = readU32BE(&bufU8[0x10]);
auto height = readU32BE(&bufU8[0x14]);
if (width == 256)
{
width = 0;
}
if (width > 255)
{
imageFailure("width exceeds U8 limit");
}
if (height == 256)
{
height = 0;
}
if (height > 255)
{
imageFailure("height exceeds U8 limit");
}
auto *entryU8 = headerU8 + 6 + image * 16;
entryU8[0] = width;
entryU8[1] = height;
entryU8[4] = 1;
entryU8[6] = 32;
if (buf.size() > UINT32_MAX)
{
imageFailure("data size exceeds U32 limit");
}
writeU32LE(&entryU8[8], uint32_t(buf.size()));
if (writeOffset > UINT32_MAX)
{
std::cerr << "output data size exceeds U32 limit" << std::endl;
exit(1);
}
writeU32LE(&entryU8[12], uint32_t(writeOffset));
}
writeHeader(); // actually write it out
return 0;
}

View File

@ -32,46 +32,65 @@ else
endif
if host_platform == 'windows'
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
generated_win_icos = {}
win_icos = {
'icon_exe': [ 'icon_exe', 'icon_exe_48', 'icon_exe_32', 'icon_exe_16' ],
'icon_cps': [ 'icon_cps', 'icon_cps_48', 'icon_cps_32', 'icon_cps_16' ],
}
foreach key, icons : win_icos
command = [
make_ico,
'@OUTPUT@',
]
foreach ikey : icons
command += [ rendered_icons[ikey] ]
windows_icons = get_option('windows_icons')
windows_utf8cp = get_option('windows_utf8cp')
rc_conf_depends = []
rc_conf_depend_files = [
'resource.h',
]
icon_exe_ico_path = ''
icon_cps_ico_path = ''
winutf8_xml_path = ''
if windows_icons
make_ico = executable('makeico', sources: 'MakeIco.cpp', native: true)
generated_win_icos = {}
win_icos = {
'icon_exe': [ 'icon_exe', 'icon_exe_48', 'icon_exe_32', 'icon_exe_16' ],
'icon_cps': [ 'icon_cps', 'icon_cps_48', 'icon_cps_32', 'icon_cps_16' ],
}
foreach key, icons : win_icos
command = [
make_ico,
'@OUTPUT@',
]
foreach ikey : icons
command += [ rendered_icons[ikey] ]
endforeach
generated_win_icos += { key: custom_target(
key + '-ico',
output: key + '.ico',
command: command,
) }
endforeach
generated_win_icos += { key: custom_target(
key + '-ico',
output: key + '.ico',
command: command,
) }
endforeach
rc_conf_depends += [
generated_win_icos['icon_exe'],
generated_win_icos['icon_cps'],
]
icon_exe_ico_path = join_paths(meson.current_build_dir(), 'icon_exe.ico')
icon_cps_ico_path = join_paths(meson.current_build_dir(), 'icon_cps.ico')
endif
if windows_utf8cp
rc_conf_depend_files += [
'winutf8.xml',
]
winutf8_xml_path = join_paths(meson.current_source_dir(), 'winutf8.xml')
endif
rc_conf_data = configuration_data()
rc_conf_data.merge_from(conf_data)
rc_conf_data.set('HAVE_ICONS', windows_icons ? 1 : 0)
rc_conf_data.set('HAVE_UTF8CP', windows_utf8cp ? 1 : 0)
rc_conf_data.set('RESOUCE_H', join_paths(meson.current_source_dir(), 'resource.h'))
rc_conf_data.set('WINUTF8_XML', join_paths(meson.current_source_dir(), 'winutf8.xml'))
rc_conf_data.set('ICON_EXE_ICO', join_paths(meson.current_build_dir(), 'icon_exe.ico'))
rc_conf_data.set('ICON_CPS_ICO', join_paths(meson.current_build_dir(), 'icon_cps.ico'))
rc_conf_data.set('WINUTF8_XML', winutf8_xml_path)
rc_conf_data.set('ICON_EXE_ICO', icon_exe_ico_path)
rc_conf_data.set('ICON_CPS_ICO', icon_cps_ico_path)
powder_files += windows_mod.compile_resources(
configure_file(
input: 'powder-res.template.rc',
output: 'powder-res.rc',
configuration: rc_conf_data,
),
depends: [
generated_win_icos['icon_exe'],
generated_win_icos['icon_cps'],
],
depend_files: [
'resource.h',
'winutf8.xml',
],
depends: rc_conf_depends,
depend_files: rc_conf_depend_files,
)
elif host_platform == 'darwin'
configure_file(

View File

@ -5,9 +5,15 @@
#include <winver.h>
#include <ntdef.h>
#define HAVE_ICONS @HAVE_ICONS@
#define HAVE_UTF8CP @HAVE_UTF8CP@
#if HAVE_ICONS
IDI_ICON ICON DISCARDABLE "@ICON_EXE_ICO@"
IDI_DOC_ICON ICON DISCARDABLE "@ICON_CPS_ICO@"
#endif
#if HAVE_UTF8CP
CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "@WINUTF8_XML@"
#endif
VS_VERSION_INFO VERSIONINFO
FILEVERSION @DISPLAY_VERSION_MAJOR@,@DISPLAY_VERSION_MINOR@,0,@BUILD_NUM@

View File

@ -51,6 +51,11 @@ inline int isign(float i)
return 0;
}
inline int iabs(int i)
{
return i * isign(i);
}
inline unsigned clamp_flt(float f, float min, float max)
{
if (f<min)

View File

@ -445,19 +445,20 @@ int Main(int argc, char *argv[])
engine.Begin();
engine.SetFastQuit(prefs.Get("FastQuit", true));
engine.TouchUI = prefs.Get("TouchUI", DEFAULT_TOUCH_UI);
engine.windowFrameOps = windowFrameOps;
SDLOpen();
if (Client::Ref().IsFirstRun() && FORCE_WINDOW_FRAME_OPS == forceWindowFrameOpsNone)
{
auto guessed = GuessBestScale();
if (windowFrameOps.scale != guessed)
if (engine.windowFrameOps.scale != guessed)
{
windowFrameOps.scale = guessed;
engine.windowFrameOps.scale = guessed;
prefs.Set("Scale", windowFrameOps.scale);
showLargeScreenDialog = true;
}
}
engine.windowFrameOps = windowFrameOps;
SDLOpen();
bool enableBluescreen = USE_BLUESCREEN && !true_arg(arguments["disable-bluescreen"]);
if (enableBluescreen)

View File

@ -75,7 +75,7 @@ int main(int argc, char * argv[])
}
else
{
std::cerr << "path to font.cpp not supplied" << std::endl;
std::cerr << "path to font.bz2 not supplied" << std::endl;
Platform::Exit(1);
}

View File

@ -22,6 +22,8 @@ int main(int argc, char *argv[])
auto inputFilename = ByteString(argv[1]);
auto outputFilename = ByteString(argv[2]) + ".png";
auto simulationData = std::make_unique<SimulationData>();
std::vector<char> fileData;
if (!Platform::ReadFile(fileData, inputFilename))
{
@ -40,7 +42,6 @@ int main(int argc, char *argv[])
throw e;
}
auto simulationData = std::make_unique<SimulationData>();
Simulation * sim = new Simulation();
Renderer * ren = new Renderer(sim);
@ -63,6 +64,7 @@ int main(int argc, char *argv[])
}
else
{
ren->clearScreen();
int w = Graphics::TextSize("Save file invalid").X + 15, x = (XRES-w)/2, y = (YRES-24)/2;
ren->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w, 24 }), 0xC0C0C0_rgb);
ren->BlendText({ x+8, y+8 }, "Save file invalid", 0xC0C0F0_rgb .WithAlpha(255));

View File

@ -349,7 +349,7 @@ static void EventProcess(const SDL_Event &event)
mousey = event.button.y;
}
mouseButton = event.button.button;
engine.onMouseClick(mousex, mousey, mouseButton);
engine.onMouseDown(mousex, mousey, mouseButton);
mouseDown = true;
if constexpr (!DEBUG)
@ -365,7 +365,7 @@ static void EventProcess(const SDL_Event &event)
mousey = event.button.y;
}
mouseButton = event.button.button;
engine.onMouseUnclick(mousex, mousey, mouseButton);
engine.onMouseUp(mousex, mousey, mouseButton);
mouseDown = false;
if constexpr (!DEBUG)

View File

@ -42,10 +42,13 @@ constexpr float AIR_PLOSS = 0.9999f;
constexpr int NGOL = 24;
constexpr int CIRCLE_BRUSH = 0;
constexpr int SQUARE_BRUSH = 1;
constexpr int TRI_BRUSH = 2;
constexpr int BRUSH_NUM = 3;
enum DefaultBrushes
{
BRUSH_CIRCLE,
BRUSH_SQUARE,
BRUSH_TRIANGLE,
NUM_DEFAULTBRUSHES,
};
//Photon constants
constexpr int SURF_RANGE = 10;

View File

@ -3,7 +3,6 @@
#include "client/http/StartupRequest.h"
#include "ClientListener.h"
#include "Format.h"
#include "MD5.h"
#include "client/GameSave.h"
#include "client/SaveFile.h"
#include "client/SaveInfo.h"
@ -118,7 +117,7 @@ void Client::Tick()
{
if (versionCheckRequest->StatusCode() == 618)
{
AddServerNotification({ "Failed to load SSL certificates", ByteString(SCHEME) + "powdertoy.co.uk/FAQ.html" });
AddServerNotification({ "Failed to load SSL certificates", ByteString::Build(SCHEME, SERVER, "/FAQ.html") });
}
try
{
@ -378,7 +377,6 @@ void Client::RescanStamps()
newStampIDs.push_back(stampID);
}
}
auto oldCount = newStampIDs.size();
auto stampIDsSet = std::set<ByteString>(stampIDs.begin(), stampIDs.end());
for (auto &stampID : stampFilesSet)
{
@ -390,8 +388,6 @@ void Client::RescanStamps()
}
if (changed)
{
// Move newly discovered stamps to front.
std::rotate(newStampIDs.begin(), newStampIDs.begin() + oldCount, newStampIDs.end());
stampIDs = newStampIDs;
WriteStamps();
}

View File

@ -51,9 +51,18 @@ GameSave::GameSave(const std::vector<char> &data, bool newWantAuthors)
void GameSave::MapPalette()
{
int partMap[PT_NUM];
bool ignoreMissingErrors[PT_NUM];
for(int i = 0; i < PT_NUM; i++)
{
partMap[i] = i;
ignoreMissingErrors[i] = false;
}
if (version <= Version(98, 2))
{
ignoreMissingErrors[PT_ICEI] = true;
ignoreMissingErrors[PT_SNOW] = true;
ignoreMissingErrors[PT_RSST] = true;
ignoreMissingErrors[PT_RSSS] = true;
}
auto &sd = SimulationData::CRef();
@ -90,12 +99,14 @@ void GameSave::MapPalette()
}
}
}
auto paletteLookup = [this, &partMap](int type) {
auto paletteLookup = [this, &partMap](int type, bool ignoreMissingErrors) {
if (type > 0 && type < PT_NUM)
{
auto carriedType = partMap[type];
if (!carriedType) // type is not 0 so this shouldn't be 0 either
{
if (ignoreMissingErrors)
return type;
missingElements.ids.insert(type);
}
type = carriedType;
@ -113,7 +124,7 @@ void GameSave::MapPalette()
{
continue;
}
tempPart.type = paletteLookup(tempPart.type);
tempPart.type = paletteLookup(tempPart.type, false);
for (auto index : possiblyCarriesType)
{
if (elements[tempPart.type].CarriesTypeIn & (1U << index))
@ -121,7 +132,7 @@ void GameSave::MapPalette()
auto *prop = reinterpret_cast<int *>(reinterpret_cast<char *>(&tempPart) + properties[index].Offset);
auto carriedType = *prop & int(pmapmask);
auto extra = *prop >> pmapbits;
carriedType = paletteLookup(carriedType);
carriedType = paletteLookup(carriedType, ignoreMissingErrors[tempPart.type]);
*prop = PMAP(extra, carriedType);
}
}
@ -2336,6 +2347,14 @@ std::pair<bool, std::vector<char>> GameSave::serialiseOPS() const
{
RESTRICTVERSION(97, 0);
}
if (particles[i].type == PT_RSST || particles[i].type == PT_RSSS)
{
RESTRICTVERSION(98, 0);
}
if (particles[i].type == PT_ETRD && (particles[i].tmp || particles[i].tmp2))
{
RESTRICTVERSION(98, 0);
}
//Get the pmap entry for the next particle in the same position
i = partsPosLink[i];

View File

@ -1,231 +0,0 @@
// based on public-domain code from Colin Plumb (1993)
#include "MD5.h"
#include <cstring>
static unsigned getu32(const unsigned char *addr)
{
return (((((unsigned long)addr[3] << 8) | addr[2]) << 8) | addr[1]) << 8 | addr[0];
}
static void putu32(unsigned data, unsigned char *addr)
{
addr[0] = (unsigned char)data;
addr[1] = (unsigned char)(data >> 8);
addr[2] = (unsigned char)(data >> 16);
addr[3] = (unsigned char)(data >> 24);
}
void md5_init(struct md5_context *ctx)
{
ctx->buf[0] = 0x67452301;
ctx->buf[1] = 0xefcdab89;
ctx->buf[2] = 0x98badcfe;
ctx->buf[3] = 0x10325476;
ctx->bits[0] = 0;
ctx->bits[1] = 0;
}
void md5_update(struct md5_context *ctx, unsigned char const *buf, unsigned len)
{
unsigned t;
// update bit count
t = ctx->bits[0];
if ((ctx->bits[0] = (t + ((unsigned)len << 3)) & 0xffffffff) < t)
ctx->bits[1]++; // carry
ctx->bits[1] += len >> 29;
t = (t >> 3) & 0x3f;
// use leading data to top up the buffer
if (t)
{
unsigned char *p = ctx->in + t;
t = 64-t;
if (len < t)
{
memcpy(p, buf, len);
return;
}
memcpy(p, buf, t);
md5_transform(ctx->buf, ctx->in);
buf += t;
len -= t;
}
// following 64-byte chunks
while (len >= 64)
{
memcpy(ctx->in, buf, 64);
md5_transform(ctx->buf, ctx->in);
buf += 64;
len -= 64;
}
// save rest of bytes for later
memcpy(ctx->in, buf, len);
}
void md5_final(unsigned char digest[16], struct md5_context *ctx)
{
unsigned count;
unsigned char *p;
// #bytes mod64
count = (ctx->bits[0] >> 3) & 0x3F;
// first char of padding = 0x80
p = ctx->in + count;
*p++ = 0x80;
// calculate # of bytes to pad
count = 64 - 1 - count;
// Pad out to 56 mod 64
if (count < 8)
{
// we need to finish a whole block before padding
memset(p, 0, count);
md5_transform(ctx->buf, ctx->in);
memset(ctx->in, 0, 56);
}
else
{
// just pad to 56 bytes
memset(p, 0, count-8);
}
// append length & final transform
putu32(ctx->bits[0], ctx->in + 56);
putu32(ctx->bits[1], ctx->in + 60);
md5_transform(ctx->buf, ctx->in);
putu32(ctx->buf[0], digest);
putu32(ctx->buf[1], digest + 4);
putu32(ctx->buf[2], digest + 8);
putu32(ctx->buf[3], digest + 12);
memset(&ctx, 0, sizeof(ctx));
}
#define F1(x, y, z) (z ^ (x & (y ^ z)))
#define F2(x, y, z) F1(z, x, y)
#define F3(x, y, z) (x ^ y ^ z)
#define F4(x, y, z) (y ^ (x | ~z))
#define MD5STEP(f, w, x, y, z, data, s) \
( w += f(x, y, z) + data, w &= 0xffffffff, w = w<<s | w>>(32-s), w += x )
void md5_transform(unsigned buf[4], const unsigned char inraw[64])
{
unsigned a, b, c, d;
unsigned in[16];
int i;
for (i = 0; i < 16; ++i)
in[i] = getu32 (inraw + 4 * i);
a = buf[0];
b = buf[1];
c = buf[2];
d = buf[3];
MD5STEP(F1, a, b, c, d, in[ 0]+0xd76aa478, 7);
MD5STEP(F1, d, a, b, c, in[ 1]+0xe8c7b756, 12);
MD5STEP(F1, c, d, a, b, in[ 2]+0x242070db, 17);
MD5STEP(F1, b, c, d, a, in[ 3]+0xc1bdceee, 22);
MD5STEP(F1, a, b, c, d, in[ 4]+0xf57c0faf, 7);
MD5STEP(F1, d, a, b, c, in[ 5]+0x4787c62a, 12);
MD5STEP(F1, c, d, a, b, in[ 6]+0xa8304613, 17);
MD5STEP(F1, b, c, d, a, in[ 7]+0xfd469501, 22);
MD5STEP(F1, a, b, c, d, in[ 8]+0x698098d8, 7);
MD5STEP(F1, d, a, b, c, in[ 9]+0x8b44f7af, 12);
MD5STEP(F1, c, d, a, b, in[10]+0xffff5bb1, 17);
MD5STEP(F1, b, c, d, a, in[11]+0x895cd7be, 22);
MD5STEP(F1, a, b, c, d, in[12]+0x6b901122, 7);
MD5STEP(F1, d, a, b, c, in[13]+0xfd987193, 12);
MD5STEP(F1, c, d, a, b, in[14]+0xa679438e, 17);
MD5STEP(F1, b, c, d, a, in[15]+0x49b40821, 22);
MD5STEP(F2, a, b, c, d, in[ 1]+0xf61e2562, 5);
MD5STEP(F2, d, a, b, c, in[ 6]+0xc040b340, 9);
MD5STEP(F2, c, d, a, b, in[11]+0x265e5a51, 14);
MD5STEP(F2, b, c, d, a, in[ 0]+0xe9b6c7aa, 20);
MD5STEP(F2, a, b, c, d, in[ 5]+0xd62f105d, 5);
MD5STEP(F2, d, a, b, c, in[10]+0x02441453, 9);
MD5STEP(F2, c, d, a, b, in[15]+0xd8a1e681, 14);
MD5STEP(F2, b, c, d, a, in[ 4]+0xe7d3fbc8, 20);
MD5STEP(F2, a, b, c, d, in[ 9]+0x21e1cde6, 5);
MD5STEP(F2, d, a, b, c, in[14]+0xc33707d6, 9);
MD5STEP(F2, c, d, a, b, in[ 3]+0xf4d50d87, 14);
MD5STEP(F2, b, c, d, a, in[ 8]+0x455a14ed, 20);
MD5STEP(F2, a, b, c, d, in[13]+0xa9e3e905, 5);
MD5STEP(F2, d, a, b, c, in[ 2]+0xfcefa3f8, 9);
MD5STEP(F2, c, d, a, b, in[ 7]+0x676f02d9, 14);
MD5STEP(F2, b, c, d, a, in[12]+0x8d2a4c8a, 20);
MD5STEP(F3, a, b, c, d, in[ 5]+0xfffa3942, 4);
MD5STEP(F3, d, a, b, c, in[ 8]+0x8771f681, 11);
MD5STEP(F3, c, d, a, b, in[11]+0x6d9d6122, 16);
MD5STEP(F3, b, c, d, a, in[14]+0xfde5380c, 23);
MD5STEP(F3, a, b, c, d, in[ 1]+0xa4beea44, 4);
MD5STEP(F3, d, a, b, c, in[ 4]+0x4bdecfa9, 11);
MD5STEP(F3, c, d, a, b, in[ 7]+0xf6bb4b60, 16);
MD5STEP(F3, b, c, d, a, in[10]+0xbebfbc70, 23);
MD5STEP(F3, a, b, c, d, in[13]+0x289b7ec6, 4);
MD5STEP(F3, d, a, b, c, in[ 0]+0xeaa127fa, 11);
MD5STEP(F3, c, d, a, b, in[ 3]+0xd4ef3085, 16);
MD5STEP(F3, b, c, d, a, in[ 6]+0x04881d05, 23);
MD5STEP(F3, a, b, c, d, in[ 9]+0xd9d4d039, 4);
MD5STEP(F3, d, a, b, c, in[12]+0xe6db99e5, 11);
MD5STEP(F3, c, d, a, b, in[15]+0x1fa27cf8, 16);
MD5STEP(F3, b, c, d, a, in[ 2]+0xc4ac5665, 23);
MD5STEP(F4, a, b, c, d, in[ 0]+0xf4292244, 6);
MD5STEP(F4, d, a, b, c, in[ 7]+0x432aff97, 10);
MD5STEP(F4, c, d, a, b, in[14]+0xab9423a7, 15);
MD5STEP(F4, b, c, d, a, in[ 5]+0xfc93a039, 21);
MD5STEP(F4, a, b, c, d, in[12]+0x655b59c3, 6);
MD5STEP(F4, d, a, b, c, in[ 3]+0x8f0ccc92, 10);
MD5STEP(F4, c, d, a, b, in[10]+0xffeff47d, 15);
MD5STEP(F4, b, c, d, a, in[ 1]+0x85845dd1, 21);
MD5STEP(F4, a, b, c, d, in[ 8]+0x6fa87e4f, 6);
MD5STEP(F4, d, a, b, c, in[15]+0xfe2ce6e0, 10);
MD5STEP(F4, c, d, a, b, in[ 6]+0xa3014314, 15);
MD5STEP(F4, b, c, d, a, in[13]+0x4e0811a1, 21);
MD5STEP(F4, a, b, c, d, in[ 4]+0xf7537e82, 6);
MD5STEP(F4, d, a, b, c, in[11]+0xbd3af235, 10);
MD5STEP(F4, c, d, a, b, in[ 2]+0x2ad7d2bb, 15);
MD5STEP(F4, b, c, d, a, in[ 9]+0xeb86d391, 21);
buf[0] += a;
buf[1] += b;
buf[2] += c;
buf[3] += d;
}
static char hexChars[] = "0123456789abcdef";
void md5_ascii(char *result, unsigned char const *buf, unsigned len)
{
struct md5_context md5;
unsigned char hash[16];
int i;
if (len==0)
len = strlen((char *)buf);
md5_init(&md5);
md5_update(&md5, buf, len);
md5_final(hash, &md5);
for (i=0; i<16; i++)
{
result[i*2] = hexChars[(hash[i]>>4)&0xF];
result[i*2+1] = hexChars[hash[i]&0x0F];
}
result[32] = 0;
}

View File

@ -1,15 +0,0 @@
#pragma once
struct md5_context
{
unsigned buf[4];
unsigned bits[2];
unsigned char in[64];
};
void md5_init(struct md5_context *context);
void md5_update(struct md5_context *context, unsigned char const *buf, unsigned len);
void md5_final(unsigned char digest[16], struct md5_context *context);
void md5_transform(unsigned buf[4], const unsigned char in[64]);
void md5_ascii(char *result, unsigned char const *buf, unsigned len);

View File

@ -14,4 +14,13 @@ namespace http
sortByVotes,
sortByDate,
};
enum Period
{
allSaves,
todaySaves,
weekSaves,
monthSaves,
yearSaves,
};
}

View File

@ -1,3 +1,4 @@
#include <ctime>
#include "SearchSavesRequest.h"
#include "Config.h"
#include "client/Client.h"
@ -6,7 +7,7 @@
namespace http
{
static ByteString Url(int start, int count, ByteString query, Sort sort, Category category)
static ByteString Url(int start, int count, ByteString query, Period period, Sort sort, Category category)
{
ByteStringBuilder builder;
builder << SCHEME << SERVER << "/Browse.json?Start=" << start << "&Count=" << count;
@ -17,6 +18,38 @@ namespace http
}
query += str;
};
time_t currentTime = time(NULL);
if(period)
{
switch (period)
{
case todaySaves:
currentTime -= 60*60*24; // One day
break;
case weekSaves:
currentTime -= 60*60*24*7; // One week
break;
case monthSaves:
currentTime -= 60*60*24*31; // One month
break;
case yearSaves:
currentTime -= 60*60*24*365; // One year
break;
default:
break;
}
struct tm currentTimeData = *localtime(&currentTime);
ByteStringBuilder afterQuery;
afterQuery << "after:" << currentTimeData.tm_year+1900 << "-" <<
(currentTimeData.tm_mon < 9 ? "0" : "") << currentTimeData.tm_mon+1 << "-" <<
(currentTimeData.tm_mday < 10 ? "0" : "") << currentTimeData.tm_mday;
appendToQuery(afterQuery.Build());
}
switch (sort)
{
case sortByDate:
@ -48,7 +81,7 @@ namespace http
return builder.Build();
}
SearchSavesRequest::SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category) : APIRequest(Url(start, count, query, sort, category), authUse, false)
SearchSavesRequest::SearchSavesRequest(int start, int count, ByteString query, Period period, Sort sort, Category category) : APIRequest(Url(start, count, query, period, sort, category), authUse, false)
{
}

View File

@ -8,7 +8,7 @@ namespace http
class SearchSavesRequest : public APIRequest
{
public:
SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category);
SearchSavesRequest(int start, int count, ByteString query, Period period, Sort sort, Category category);
std::pair<int, std::vector<std::unique_ptr<SaveInfo>>> Finish();
};

View File

@ -52,6 +52,10 @@ namespace http
return;
}
auto &info = versions[key];
if (info.isNull())
{
return;
}
auto getOr = [&info](ByteString key, int defaultValue) -> int {
if (!info.isMember(key))
{
@ -59,7 +63,7 @@ namespace http
}
return info[key].asInt();
};
auto build = getOr(key == "Snapshot" ? "Snapshot" : "Build", -1);
auto build = getOr(key == "Snapshot" ? "Snapshot" : "Build", 0);
if (!updateAvailableFunc(build))
{
return;
@ -68,8 +72,8 @@ namespace http
channel,
ByteString::Build(SCHEME, alternate ? UPDATESERVER : SERVER, info["File"].asString()),
ByteString(info["Changelog"].asString()).FromUtf8(),
getOr("Major", -1),
getOr("Minor", -1),
getOr("Major", 0),
getOr("Minor", 0),
build,
};
};

View File

@ -1,5 +1,4 @@
client_files = files(
'MD5.cpp',
'SaveFile.cpp',
'SaveInfo.cpp',
'ThumbnailRendererTask.cpp',

View File

@ -112,6 +112,14 @@ struct Vec2
);
}
Vec2<T> Min(Vec2<T> other) const
{
return Vec2<T>(
std::min(X, other.X),
std::min(Y, other.Y)
);
}
// Return a rectangle starting at origin, whose dimensions match this vector
template<typename S = T, typename = std::enable_if_t<std::is_integral_v<S>>>
constexpr inline Rect<T> OriginRect() const

View File

@ -58,7 +58,7 @@ bool ReadFile(std::vector<char> &fileData, ByteString filename)
if (f) f.seekg(0, std::ios::end);
if (f) fileData.resize(f.tellg());
if (f) f.seekg(0);
if (f) f.read(&fileData[0], fileData.size());
if (f && fileData.size()) f.read(&fileData[0], fileData.size());
if (!f)
{
std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl;

View File

@ -6,6 +6,9 @@
#include "Config.h"
#include <cstring>
#include <ctime>
#ifdef __FreeBSD__
# include <sys/sysctl.h>
#endif
namespace Platform
{
@ -26,7 +29,26 @@ long unsigned int GetTime()
ByteString ExecutableNameFirstApprox()
{
return "/proc/self/exe";
if (Stat("/proc/self/exe"))
{
return "/proc/self/exe";
}
#ifdef __FreeBSD__
{
int mib[4];
mib[0] = CTL_KERN;
mib[1] = KERN_PROC;
mib[2] = KERN_PROC_PATHNAME;
mib[3] = -1;
std::array<char, 1000> buf;
size_t cb = buf.size();
if (!sysctl(mib, 4, &buf[0], &cb, NULL, 0))
{
return ByteString(&buf[0], &buf[0] + cb);
}
}
#endif
return "";
}
bool CanUpdate()

View File

@ -1,5 +1,6 @@
#pragma once
#include "common/String.h"
#include <cstdint>
#include <string>
#include <vector>
#include <optional>

View File

@ -16,6 +16,11 @@ elif host_platform == 'linux'
stacktrace_files = files('Execinfo.cpp')
# export symbols so backtrace_symbols works, see above
bluescreen_export_symbols = true
if host_machine.system() in [ 'freebsd' ]
project_deps += [
c_compiler.find_library('execinfo'),
]
endif
else
stacktrace_files = files('Null.cpp')
endif

View File

@ -183,6 +183,8 @@ void Renderer::render_parts()
gfctx.pipeSubcallTpart = nullptr;
int deca, decr, decg, decb, cola, colr, colg, colb, firea, firer, fireg, fireb, pixel_mode, q, i, t, nx, ny, x, y;
int orbd[4] = {0, 0, 0, 0}, orbl[4] = {0, 0, 0, 0};
int drawing_budget = 1000000; //Serves as an upper bound for costly effects such as SPARK, FLARE and LFLARE
if(!sim)
return;
auto *parts = sim->parts;
@ -633,7 +635,7 @@ void Renderer::render_parts()
{
auto flicker = float(gfctx.rng()%20);
auto gradv = 4*sim->parts[i].life + flicker;
for (x = 0; gradv>0.5; x++) {
for (x = 0; (gradv>0.5) && (drawing_budget > 0); x++) {
auto col = RGBA<uint8_t>(
std::min(0xFF, colr * int(gradv) / 255),
std::min(0xFF, colg * int(gradv) / 255),
@ -644,6 +646,7 @@ void Renderer::render_parts()
AddPixel({ nx, ny+x }, col);
AddPixel({ nx, ny-x }, col);
gradv = gradv/1.5f;
drawing_budget--;
}
}
if(pixel_mode & PMODE_FLARE)
@ -660,12 +663,13 @@ void Renderer::render_parts()
BlendPixel({ nx-1, ny-1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx+1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx-1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
for (x = 1; gradv>0.5; x++) {
for (x = 1; (gradv>0.5) && (drawing_budget > 0); x++) {
AddPixel({ nx+x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx-x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny+x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny-x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
gradv = gradv/1.2f;
drawing_budget--;
}
}
if(pixel_mode & PMODE_LFLARE)
@ -682,12 +686,13 @@ void Renderer::render_parts()
BlendPixel({ nx-1, ny-1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx+1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
BlendPixel({ nx-1, ny+1 }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
for (x = 1; gradv>0.5; x++) {
for (x = 1; (gradv>0.5) && (drawing_budget > 0); x++) {
AddPixel({ nx+x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx-x, ny }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny+x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
AddPixel({ nx, ny-x }, RGBA<uint8_t>(colr, colg, colb, int(gradv)));
gradv = gradv/1.01f;
drawing_budget--;
}
}
if (pixel_mode & EFFECT_GRAVIN)

View File

@ -41,6 +41,8 @@ class Renderer: public RasterDrawMethods<Renderer>
friend struct RasterDrawMethods<Renderer>;
float fireIntensity = 1;
public:
Vec2<int> Size() const
{
@ -97,6 +99,10 @@ public:
void DrawSigns();
void render_gravlensing(const Video &source);
void render_fire();
float GetFireIntensity() const
{
return fireIntensity;
}
void prepare_alpha(int size, float intensity);
void render_parts();
void draw_grav_zones();

View File

@ -11,7 +11,6 @@ constexpr auto VIDYRES = WINDOWH;
void Renderer::RenderBegin()
{
draw_air();
draw_grav();
DrawWalls();
render_parts();
@ -142,9 +141,10 @@ float glow_alphaf[11][11];
float blur_alphaf[7][7];
void Renderer::prepare_alpha(int size, float intensity)
{
fireIntensity = intensity;
//TODO: implement size
int x,y,i,j;
float multiplier = 255.0f*intensity;
float multiplier = 255.0f*fireIntensity;
memset(temp, 0, sizeof(temp));
for (x=0; x<CELL; x++)

View File

@ -6,6 +6,7 @@
#include <SDL.h>
#include "gui/interface/Textbox.h"
#include "gui/interface/ScrollPanel.h"
#include "gui/interface/Label.h"
#include "gui/game/Tool.h"
#include "gui/game/Menu.h"
@ -52,6 +53,9 @@ ElementSearchActivity::ElementSearchActivity(GameController * gameController, st
AddComponent(okButton);
AddComponent(closeButton);
scrollPanel = new ui::ScrollPanel(searchField->Position + Vec2{ 1, searchField->Size.Y+9 }, { searchField->Size.X - 2, Size.Y-(searchField->Position.Y+searchField->Size.Y+6)-23 });
AddComponent(scrollPanel);
searchTools("");
}
@ -59,12 +63,12 @@ void ElementSearchActivity::searchTools(String query)
{
firstResult = NULL;
for (auto &toolButton : toolButtons) {
RemoveComponent(toolButton);
scrollPanel->RemoveChild(toolButton);
delete toolButton;
}
toolButtons.clear();
ui::Point viewPosition = searchField->Position + ui::Point(2+0, searchField->Size.Y+2+8);
ui::Point viewPosition = { 1, 1 };
ui::Point current = ui::Point(0, 0);
String queryLower = query.ToLower();
@ -178,7 +182,7 @@ void ElementSearchActivity::searchTools(String query)
}
toolButtons.push_back(tempButton);
AddComponent(tempButton);
scrollPanel->AddChild(tempButton);
current.X += 31;
@ -186,10 +190,13 @@ void ElementSearchActivity::searchTools(String query)
current.X = 0;
current.Y += 19;
}
if(current.Y + viewPosition.Y + 18 > Size.Y-23)
break;
}
if (current.X == 0)
{
current.Y -= 19;
}
scrollPanel->InnerSize = ui::Point(scrollPanel->Size.X, current.Y + 20);
}
void ElementSearchActivity::SetActiveTool(int selectionState, Tool * tool)
@ -216,8 +223,7 @@ void ElementSearchActivity::OnDraw()
g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb);
g->BlendRect(
RectSized(Position + searchField->Position + Vec2{ 0, searchField->Size.Y+8 },
{ searchField->Size.X, Size.Y-(searchField->Position.Y+searchField->Size.Y+8)-23 }),
RectSized(Position + scrollPanel->Position - Vec2{ 1, 1 }, scrollPanel->Size + Vec2{ 2, 2 }),
0xFFFFFF_rgb .WithAlpha(180));
if (toolTipPresence && toolTip.length())
{

View File

@ -10,6 +10,7 @@ class GameController;
namespace ui
{
class ScrollPanel;
class Textbox;
}
@ -20,6 +21,7 @@ class ElementSearchActivity: public WindowActivity
std::vector<Tool*> tools;
ui::Textbox * searchField;
std::vector<ToolButton*> toolButtons;
ui::ScrollPanel *scrollPanel = nullptr;
String toolTip;
int toolTipPresence;
bool shiftPressed;

View File

@ -39,7 +39,7 @@ class LoadFilesTask: public Task
bool doWork() override
{
std::vector<ByteString> files = Platform::DirectorySearch(directory, search, { ".cps" });
std::sort(files.rbegin(), files.rend(), [](ByteString a, ByteString b) { return a.ToLower() < b.ToLower(); });
std::sort(files.rbegin(), files.rend(), [](ByteString a, ByteString b) { return a.ToLower() > b.ToLower(); });
notifyProgress(-1);
for(std::vector<ByteString>::iterator iter = files.begin(), end = files.end(); iter != end; ++iter)

View File

@ -52,6 +52,14 @@ void Brush::InitOutline()
void Brush::SetRadius(ui::Point newRadius)
{
if (newRadius.X < 0)
newRadius.X = 0;
if (newRadius.Y < 0)
newRadius.Y = 0;
if (newRadius.X > 200)
newRadius.X = 200;
if (newRadius.Y > 200)
newRadius.Y = 200;
radius = newRadius;
InitOutline();
}
@ -67,14 +75,6 @@ void Brush::AdjustSize(int delta, bool logarithmic, bool keepX, bool keepY)
newSize = oldSize + ui::Point(delta * std::max(oldSize.X / 5, 1), delta * std::max(oldSize.Y / 5, 1));
else
newSize = oldSize + ui::Point(delta, delta);
if (newSize.X < 0)
newSize.X = 0;
if (newSize.Y < 0)
newSize.Y = 0;
if (newSize.X > 200)
newSize.X = 200;
if (newSize.Y > 200)
newSize.Y = 200;
if (keepY)
SetRadius(ui::Point(newSize.X, oldSize.Y));

View File

@ -90,7 +90,7 @@ GameController::GameController():
gameView->SetDebugHUD(GlobalPrefs::Ref().Get("Renderer.DebugMode", false));
CommandInterface::Create(this, gameModel);
commandInterface = CommandInterface::Create(this, gameModel);
Client::Ref().AddListener(this);
@ -146,7 +146,7 @@ GameController::~GameController()
{
delete *iter;
}
delete commandInterface;
commandInterface.reset();
delete gameModel;
if (gameView->CloseActiveWindow())
{
@ -552,7 +552,7 @@ bool GameController::MouseUp(int x, int y, unsigned button, MouseupReason reason
}
break;
case sign::Type::Thread:
Platform::OpenURI(ByteString::Build(SCHEME, "powdertoy.co.uk/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8()));
Platform::OpenURI(ByteString::Build(SCHEME, SERVER, "/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8()));
break;
case sign::Type::Search:
OpenSearch(str.Substr(3, si.first - 3));
@ -785,7 +785,7 @@ void GameController::ResetSpark()
void GameController::SwitchGravity()
{
gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode + 1) % NUM_GRAV_MODES;
gameModel->GetSimulation()->gravityMode = (gameModel->GetSimulation()->gravityMode + 1) % NUM_GRAVMODES;
switch (gameModel->GetSimulation()->gravityMode)
{
@ -806,23 +806,23 @@ void GameController::SwitchGravity()
void GameController::SwitchAir()
{
gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode + 1) % NUM_AIR_MODES;
gameModel->GetSimulation()->air->airMode = (gameModel->GetSimulation()->air->airMode + 1) % NUM_AIRMODES;
switch (gameModel->GetSimulation()->air->airMode)
{
case AIR_ON:
gameModel->SetInfoTip("Air: On");
break;
case AIR_PRESSURE_OFF:
case AIR_PRESSUREOFF:
gameModel->SetInfoTip("Air: Pressure Off");
break;
case AIR_VELOCITY_OFF:
case AIR_VELOCITYOFF:
gameModel->SetInfoTip("Air: Velocity Off");
break;
case AIR_OFF:
gameModel->SetInfoTip("Air: Off");
break;
case AIR_NO_UPDATE:
case AIR_NOUPDATE:
gameModel->SetInfoTip("Air: No Update");
break;
}
@ -1031,7 +1031,7 @@ int GameController::GetEdgeMode()
void GameController::SetEdgeMode(int edgeMode)
{
if (edgeMode < 0 || edgeMode >= NUM_EDGE_MODES)
if (edgeMode < 0 || edgeMode >= NUM_EDGEMODES)
edgeMode = 0;
gameModel->SetEdgeMode(edgeMode);
@ -1111,8 +1111,11 @@ void GameController::SetActiveTool(int toolSelection, Tool * tool)
gameModel->SetLastTool(tool);
for(int i = 0; i < 3; i++)
{
if(gameModel->GetActiveTool(i) == gameModel->GetMenuList().at(SC_WALL)->GetToolList().at(WL_GRAV))
auto *activeTool = gameModel->GetActiveTool(i);
if (activeTool && activeTool->Identifier == "DEFAULT_WL_GRVTY")
{
gameModel->GetRenderer()->gravityZonesEnabled = true;
}
}
if (tool->Identifier == "DEFAULT_UI_PROPERTY")
{
@ -1386,7 +1389,7 @@ void GameController::OpenOptions()
void GameController::ShowConsole()
{
if (!console)
console = new ConsoleController(NULL, commandInterface);
console = new ConsoleController(NULL, commandInterface.get());
if (console->GetView() != ui::Engine::Ref().GetWindow())
ui::Engine::Ref().ShowWindow(console->GetView());
}

View File

@ -1,4 +1,5 @@
#pragma once
#include "lua/CommandInterfacePtr.h"
#include "client/ClientListener.h"
#include "client/StartupInfo.h"
#include "gui/interface/Point.h"
@ -28,7 +29,6 @@ class LocalBrowserController;
class SearchController;
class PreviewController;
class RenderController;
class CommandInterface;
class VideoBuffer;
class Tool;
class Menu;
@ -39,6 +39,8 @@ class TagsController;
class ConsoleController;
class GameController: public ClientListener
{
CommandInterfacePtr commandInterface;
private:
bool firstTick;
int foundSignID;
@ -126,6 +128,7 @@ public:
int GetEdgeMode();
void SetEdgeMode(int edgeMode);
void SetDebugFlags(unsigned int flags) { debugFlags = flags; }
unsigned int GetDebugFlags() const { return debugFlags; }
void SetActiveMenu(int menuID);
std::vector<Menu*> GetMenuList();
int GetNumMenus(bool onlyEnabled);

View File

@ -54,16 +54,16 @@ GameModel::GameModel():
colour(255, 0, 0, 255),
edgeMode(EDGE_VOID),
ambientAirTemp(R_TEMP + 273.15f),
decoSpace(0)
decoSpace(DECOSPACE_SRGB)
{
sim = new Simulation();
sim->useLuaCallbacks = true;
ren = new Renderer(sim);
activeTools = regularToolset;
activeTools = &regularToolset[0];
std::fill(decoToolset, decoToolset+4, (Tool*)NULL);
std::fill(regularToolset, regularToolset+4, (Tool*)NULL);
std::fill(decoToolset.begin(), decoToolset.end(), nullptr);
std::fill(regularToolset.begin(), regularToolset.end(), nullptr);
//Default render prefs
ren->SetRenderMode({
@ -93,7 +93,7 @@ GameModel::GameModel():
ren->decorations_enable = prefs.Get("Renderer.Decorations", true);
//Load config into simulation
edgeMode = prefs.Get("Simulation.EdgeMode", (int)EDGE_VOID);
edgeMode = prefs.Get("Simulation.EdgeMode", NUM_EDGEMODES, EDGE_VOID);
sim->SetEdgeMode(edgeMode);
ambientAirTemp = float(R_TEMP) + 273.15f;
{
@ -104,9 +104,9 @@ GameModel::GameModel():
}
}
sim->air->ambientAirTemp = ambientAirTemp;
decoSpace = prefs.Get("Simulation.DecoSpace", 0); // TODO: DecoSpace enum
decoSpace = prefs.Get("Simulation.DecoSpace", NUM_DECOSPACES, DECOSPACE_SRGB);
sim->SetDecoSpace(decoSpace);
int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", 0); // TODO: NewtonianGravity enum
int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", NUM_GRAVMODES, GRAV_VERTICAL);
if (ngrav_enable)
sim->grav->start_grav_async();
sim->aheat_enable = prefs.Get("Simulation.AmbientHeat", 0); // TODO: AmbientHeat enum
@ -228,7 +228,7 @@ void GameModel::BuildMenus()
if(activeMenu != -1)
lastMenu = activeMenu;
ByteString activeToolIdentifiers[4];
std::array<ByteString, NUM_TOOLINDICES> activeToolIdentifiers;
if(regularToolset[0])
activeToolIdentifiers[0] = regularToolset[0]->Identifier;
if(regularToolset[1])
@ -881,17 +881,17 @@ void GameModel::SetActiveMenu(int menuID)
if(menuID == SC_DECO)
{
if(activeTools != decoToolset)
if(activeTools != &decoToolset[0])
{
activeTools = decoToolset;
activeTools = &decoToolset[0];
notifyActiveToolsChanged();
}
}
else
{
if(activeTools != regularToolset)
if(activeTools != &regularToolset[0])
{
activeTools = regularToolset;
activeTools = &regularToolset[0];
notifyActiveToolsChanged();
}
}
@ -1712,7 +1712,7 @@ void GameModel::BeforeSim()
{
if (!sim->sys_pause || sim->framerender)
{
commandInterface->HandleEvent(BeforeSimEvent{});
CommandInterface::Ref().HandleEvent(BeforeSimEvent{});
}
sim->BeforeSim();
}
@ -1720,5 +1720,5 @@ void GameModel::BeforeSim()
void GameModel::AfterSim()
{
sim->AfterSim();
commandInterface->HandleEvent(AfterSimEvent{});
CommandInterface::Ref().HandleEvent(AfterSimEvent{});
}

View File

@ -6,6 +6,9 @@
#include <deque>
#include <memory>
#include <optional>
#include <array>
constexpr auto NUM_TOOLINDICES = 4;
class Menu;
class Tool;
@ -72,8 +75,8 @@ private:
std::unique_ptr<SaveFile> currentFile;
Tool * lastTool;
Tool ** activeTools;
Tool * decoToolset[4];
Tool * regularToolset[4];
std::array<Tool *, NUM_TOOLINDICES> decoToolset;
std::array<Tool *, NUM_TOOLINDICES> regularToolset;
User currentUser;
float toolStrength;
std::deque<HistoryEntry> history;
@ -192,6 +195,10 @@ public:
Brush &GetBrush();
Brush *GetBrushByID(int i);
int GetBrushID();
int BrushListSize() const
{
return int(brushList.size());
}
void SetBrushID(int i);
void SetVote(int direction);

View File

@ -87,7 +87,7 @@ public:
}
}
}
void OnMouseUnclick(int x, int y, unsigned int button) override
void OnMouseClick(int x, int y, unsigned int button) override
{
if(isButtonDown)
{
@ -96,7 +96,7 @@ public:
else if(rightDown)
DoRightAction();
}
ui::Button::OnMouseUnclick(x, y, button);
ui::Button::OnMouseClick(x, y, button);
}
void OnMouseHover(int x, int y) override
@ -120,15 +120,18 @@ public:
toolTip = newToolTip1;
toolTip2 = newToolTip2;
}
void OnMouseClick(int x, int y, unsigned int button) override
void OnMouseDown(int x, int y, unsigned int button) override
{
ui::Button::OnMouseClick(x, y, button);
rightDown = false;
leftDown = false;
if(x >= splitPosition)
rightDown = true;
else if(x < splitPosition)
leftDown = true;
ui::Button::OnMouseDown(x, y, button);
if (MouseDownInside)
{
rightDown = false;
leftDown = false;
if(x - Position.X >= splitPosition)
rightDown = true;
else if(x - Position.X < splitPosition)
leftDown = true;
}
}
void DoRightAction()
{
@ -1052,10 +1055,17 @@ void GameView::updateToolButtonScroll()
{
for (auto *button : toolButtons)
{
if (button->Position.X < x && button->Position.X + button->Size.X > x)
auto inside = button->Position.X < x && button->Position.X + button->Size.X > x;
if (inside && !button->MouseInside)
{
button->MouseInside = true;
button->OnMouseEnter(x, y);
else
}
if (!inside && button->MouseInside)
{
button->MouseInside = false;
button->OnMouseLeave(x, y);
}
}
}
}
@ -2124,6 +2134,7 @@ void GameView::OnDraw()
auto &sd = SimulationData::Ref();
std::unique_lock lk(sd.elementGraphicsMx);
ren->clearScreen();
ren->draw_air();
c->BeforeSimDraw();
ren->RenderBegin();
ren->SetSample(c->PointTranslate(currentMouse));

View File

@ -76,7 +76,7 @@ inline ByteString IntroText()
}
else
{
sb << "\bgTo use online features such as saving, you need to register at: \brhttps://powdertoy.co.uk/Register.html\n";
sb << "\bgTo use online features such as saving, you need to register at: \brhttps://" << SERVER << "/Register.html\n";
}
sb << "\n\bt" << VersionInfo();
return sb.Build();

View File

@ -16,12 +16,15 @@ ToolButton::ToolButton(ui::Point position, ui::Point size, String text, ByteStri
Component::TextPosition(buttonDisplayText);
}
void ToolButton::OnMouseClick(int x, int y, unsigned int button)
void ToolButton::OnMouseDown(int x, int y, unsigned int button)
{
isButtonDown = true;
if (MouseDownInside)
{
isButtonDown = true;
}
}
void ToolButton::OnMouseUnclick(int x, int y, unsigned int button)
void ToolButton::OnMouseClick(int x, int y, unsigned int button)
{
if(isButtonDown)
{

View File

@ -9,7 +9,7 @@ class ToolButton: public ui::Button
ByteString toolIdentifier;
public:
ToolButton(ui::Point position, ui::Point size, String text, ByteString toolIdentifier, String toolTip = String());
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned int button) override;
void OnMouseClick(int x, int y, unsigned int button) override;
void Draw(const ui::Point& screenPos) override;

View File

@ -51,7 +51,7 @@ void AvatarButton::Draw(const Point& screenPos)
}
}
void AvatarButton::OnMouseUnclick(int x, int y, unsigned int button)
void AvatarButton::OnMouseClick(int x, int y, unsigned int button)
{
if(button != 1)
{
@ -70,16 +70,19 @@ void AvatarButton::OnContextMenuAction(int item)
//Do nothing
}
void AvatarButton::OnMouseClick(int x, int y, unsigned int button)
void AvatarButton::OnMouseDown(int x, int y, unsigned int button)
{
if(button == SDL_BUTTON_RIGHT)
if (MouseDownInside)
{
if(menu)
menu->Show(GetScreenPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
if(button == SDL_BUTTON_RIGHT)
{
if(menu)
menu->Show(GetContainerPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
}
}
}

View File

@ -30,7 +30,7 @@ public:
virtual ~AvatarButton() = default;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;
void OnMouseLeave(int x, int y) override;

View File

@ -86,7 +86,7 @@ void Button::Draw(const Point& screenPos)
if (Enabled)
{
if (isButtonDown || (isTogglable && toggle))
if ((isButtonDown && MouseDownInside) || (isTogglable && toggle))
{
textColour = Appearance.TextActive;
borderColour = Appearance.BorderActive;
@ -140,7 +140,7 @@ void Button::Draw(const Point& screenPos)
}
}
void Button::OnMouseUnclick(int x, int y, unsigned int button)
void Button::OnMouseClick(int x, int y, unsigned int button)
{
if(button == 1)
{
@ -171,17 +171,20 @@ void Button::OnMouseUp(int x, int y, unsigned int button)
isAltButtonDown = false;
}
void Button::OnMouseClick(int x, int y, unsigned int button)
void Button::OnMouseDown(int x, int y, unsigned int button)
{
if(!Enabled)
return;
if(button == 1)
if (MouseDownInside)
{
isButtonDown = true;
}
else if(button == 3)
{
isAltButtonDown = true;
if(!Enabled)
return;
if(button == 1)
{
isButtonDown = true;
}
else if(button == 3)
{
isAltButtonDown = true;
}
}
}

View File

@ -18,7 +18,7 @@ public:
virtual ~Button() = default;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;

View File

@ -124,17 +124,21 @@ void Component::SetParent(Panel* new_parent)
this->_parent = new_parent;
}
Point Component::GetScreenPos()
Point Component::GetContainerPos()
{
Point newPos(0,0);
if(GetParentWindow())
newPos += GetParentWindow()->Position;
if(GetParent())
newPos += GetParent()->Position + GetParent()->ViewportPosition;
newPos += Position;
return newPos;
}
Point Component::GetScreenPos()
{
return GetContainerPos() + Position;
}
Graphics * Component::GetGraphics()
{
return parentstate_->GetGraphics();
@ -185,11 +189,7 @@ void Component::OnMouseHover(int localx, int localy)
{
}
void Component::OnMouseMoved(int localx, int localy, int dx, int dy)
{
}
void Component::OnMouseMovedInside(int localx, int localy, int dx, int dy)
void Component::OnMouseMoved(int localx, int localy)
{
}
@ -201,10 +201,6 @@ void Component::OnMouseLeave(int localx, int localy)
{
}
void Component::OnMouseUnclick(int localx, int localy, unsigned button)
{
}
void Component::OnMouseUp(int x, int y, unsigned button)
{
}

View File

@ -43,6 +43,8 @@ namespace ui
bool Enabled;
bool Visible;
bool DoesTextInput;
bool MouseInside;
bool MouseDownInside;
ui::Appearance Appearance;
//virtual void SetAppearance(ui::Appearance);
@ -51,6 +53,7 @@ namespace ui
void Refresh();
Point GetContainerPos();
Point GetScreenPos();
/* See the parent of this component.
@ -94,20 +97,8 @@ namespace ui
// Params:
// localx: Local mouse X position.
// localy: Local mouse Y position.
// dx: Mouse X delta.
// dy: Mouse Y delta.
///
virtual void OnMouseMoved(int localx, int localy, int dx, int dy);
///
// Called: When the mouse moves.
// Params:
// localx: Local mouse X position.
// localy: Local mouse Y position.
// dx: Mouse X delta.
// dy: Mouse Y delta.
///
virtual void OnMouseMovedInside(int localx, int localy, int dx, int dy);
virtual void OnMouseMoved(int localx, int localy);
///
// Called: When the mouse moves on top of the item.
@ -146,7 +137,7 @@ namespace ui
virtual void OnMouseUp(int x, int y, unsigned button);
///
// Called: When a mouse button is pressed on top of the item.
// Called: When a mouse button is pressed and then released on top of the item.
// Params:
// x: X position of the mouse.
// y: Y position of the mouse.
@ -154,15 +145,6 @@ namespace ui
///
virtual void OnMouseClick(int localx, int localy, unsigned button);
///
// Called: When a mouse button is released on top of the item.
// Params:
// x: X position of the mouse.
// y: Y position of the mouse.
// button: The button that is being released.
///
virtual void OnMouseUnclick(int localx, int localy, unsigned button);
///
// Called: When the mouse wheel moves/changes.
// Params:

View File

@ -133,7 +133,7 @@ void DirectionSelector::Draw(const ui::Point& screenPos)
g->BlendEllipse(center + value.offset, { handleRadius, handleRadius }, borderColor);
}
void DirectionSelector::OnMouseMoved(int x, int y, int dx, int dy)
void DirectionSelector::OnMouseMoved(int x, int y)
{
if (mouseDown)
{
@ -142,11 +142,14 @@ void DirectionSelector::OnMouseMoved(int x, int y, int dx, int dy)
CheckHovering(x, y);
}
void DirectionSelector::OnMouseClick(int x, int y, unsigned button)
void DirectionSelector::OnMouseDown(int x, int y, unsigned button)
{
mouseDown = true;
SetPositionAbs({ x, y });
CheckHovering(x, y);
if (MouseDownInside)
{
mouseDown = true;
SetPositionAbs({ x - Position.X, y - Position.Y });
CheckHovering(x - Position.X, y - Position.Y);
}
}
void DirectionSelector::OnMouseUp(int x, int y, unsigned button)

View File

@ -78,8 +78,8 @@ public:
void SetValues(float x, float y);
void Draw(const ui::Point& screenPos) override;
void OnMouseMoved(int x, int y, int dx, int dy) override;
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseMoved(int x, int y) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseUp(int x, int y, unsigned button) override;
inline void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override { altDown = alt; }
inline void OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override { altDown = alt; }

View File

@ -15,7 +15,8 @@ class DropDownWindow : public ui::Window
public:
DropDownWindow(DropDown * dropDown):
Window(dropDown->GetScreenPos() + ui::Point(-1, -1 - dropDown->optionIndex * 16), ui::Point(dropDown->Size.X+2, 2+dropDown->options.size()*16)),
Window(dropDown->GetScreenPos() + ui::Point(-1, -1 - (dropDown->optionIndex*16 < dropDown->GetScreenPos().Y ? dropDown->optionIndex*16 : 0)),
ui::Point(dropDown->Size.X+2, 2+dropDown->options.size()*16)),
dropDown(dropDown),
appearance(dropDown->Appearance)
{

View File

@ -83,6 +83,8 @@ void Engine::ShowWindow(Window * window)
{
window->Position.Y = (g->Size().Y - window->Size.Y) / 2;
}
window->Size = window->Size.Min(g->Size());
window->Position = window->Position.Clamp(RectBetween<int>({0, 0}, g->Size()));
/*if(window->Position.Y > 0)
{
windowTargetPosition = window->Position;
@ -268,14 +270,14 @@ void Engine::onTextEditing(String text, int start)
}
}
void Engine::onMouseClick(int x, int y, unsigned button)
void Engine::onMouseDown(int x, int y, unsigned button)
{
mouseb_ |= button;
if (state_ && !ignoreEvents)
state_->DoMouseDown(x, y, button);
}
void Engine::onMouseUnclick(int x, int y, unsigned button)
void Engine::onMouseUp(int x, int y, unsigned button)
{
mouseb_ &= ~button;
if (state_ && !ignoreEvents)

View File

@ -30,8 +30,8 @@ namespace ui
void initialMouse(int x, int y);
void onMouseMove(int x, int y);
void onMouseClick(int x, int y, unsigned button);
void onMouseUnclick(int x, int y, unsigned button);
void onMouseDown(int x, int y, unsigned button);
void onMouseUp(int x, int y, unsigned button);
void onMouseWheel(int x, int y, int delta);
void onKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);
void onKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);

View File

@ -88,24 +88,27 @@ void Label::OnContextMenuAction(int item)
}
}
void Label::OnMouseClick(int x, int y, unsigned button)
void Label::OnMouseDown(int x, int y, unsigned button)
{
if(button == SDL_BUTTON_RIGHT)
if (MouseDownInside)
{
if (menu)
if(button == SDL_BUTTON_RIGHT)
{
menu->Show(GetScreenPos() + ui::Point(x, y));
if (menu)
{
menu->Show(GetContainerPos() + ui::Point(x, y));
}
}
}
else
{
selecting = true;
auto tp = textPosition - Vec2{ scrollX, 0 };
selectionIndex0 = textWrapper.Point2Index(x - tp.X, y - tp.Y);
selectionIndexL = selectionIndex0;
selectionIndexH = selectionIndex0;
else
{
selecting = true;
auto tp = textPosition - Vec2{ scrollX, 0 };
selectionIndex0 = textWrapper.Point2Index(x - Position.X - tp.X, y - Position.Y - tp.Y);
selectionIndexL = selectionIndex0;
selectionIndexH = selectionIndex0;
updateSelection();
updateSelection();
}
}
}
@ -143,7 +146,7 @@ void Label::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bo
}
}
void Label::OnMouseMoved(int localx, int localy, int dx, int dy)
void Label::OnMouseMoved(int localx, int localy)
{
if (selecting)
{

View File

@ -58,9 +58,9 @@ namespace ui
void SetTextColour(Colour textColour) { this->textColour = textColour; }
void OnContextMenuAction(int item) override;
virtual void OnMouseClick(int x, int y, unsigned button) override;
virtual void OnMouseDown(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void OnMouseMoved(int localx, int localy, int dx, int dy) override;
void OnMouseMoved(int localx, int localy) override;
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
void Draw(const Point& screenPos) override;
void Tick(float dt) override;

View File

@ -10,8 +10,7 @@ using namespace ui;
Panel::Panel(Point position, Point size):
Component(position, size),
InnerSize(size),
ViewportPosition(0, 0),
mouseInside(false)
ViewportPosition(0, 0)
{
}
@ -27,6 +26,8 @@ void Panel::AddChild(Component* c)
{
c->SetParent(this);
c->SetParentWindow(this->GetParentWindow());
c->MouseInside = false;
c->MouseDownInside = false;
}
int Panel::GetChildCount()
@ -108,43 +109,40 @@ void Panel::OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl,
void Panel::OnMouseClick(int localx, int localy, unsigned button)
{
bool childclicked = false;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
if(children[i]->Enabled)
{
//is mouse inside?
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
childclicked = true;
GetParentWindow()->FocusComponent(children[i]);
children[i]->OnMouseClick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
break;
}
}
}
//if a child wasn't clicked, send click to ourself
if(!childclicked)
{
XOnMouseClick(localx, localy, button);
GetParentWindow()->FocusComponent(this);
}
XOnMouseClick(localx, localy, button);
}
void Panel::OnMouseDown(int x, int y, unsigned button)
{
XOnMouseDown(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
if (MouseDownInside)
{
if(children[i]->Enabled)
children[i]->OnMouseDown(x, y, button);
auto localx = x - Position.X;
auto localy = y - Position.Y;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
if(children[i]->Enabled)
{
//is mouse inside?
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
GetParentWindow()->FocusComponent(children[i]);
children[i]->MouseDownInside = true;
break;
}
}
}
XOnMouseDown(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
{
if(children[i]->Enabled)
children[i]->OnMouseDown(x - Position.X - ViewportPosition.X, y - Position.Y - ViewportPosition.Y, button);
}
}
}
@ -155,12 +153,14 @@ void Panel::OnMouseHover(int localx, int localy)
{
if (children[i]->Enabled)
{
if( localx >= children[i]->Position.X &&
localy >= children[i]->Position.Y &&
localx < children[i]->Position.X + children[i]->Size.X &&
localy < children[i]->Position.Y + children[i]->Size.Y )
auto px = children[i]->Position.X + ViewportPosition.X;
auto py = children[i]->Position.Y + ViewportPosition.Y;
if( localx >= px &&
localy >= py &&
localx < px + children[i]->Size.X &&
localy < py + children[i]->Size.Y )
{
children[i]->OnMouseHover(localx - children[i]->Position.X, localy - children[i]->Position.Y);
children[i]->OnMouseHover(localx - px, localy - py);
break;
}
}
@ -170,25 +170,26 @@ void Panel::OnMouseHover(int localx, int localy)
XOnMouseHover(localx, localy);
}
void Panel::OnMouseMoved(int localx, int localy, int dx, int dy)
void Panel::OnMouseMoved(int localx, int localy)
{
XOnMouseMoved(localx, localy, dx, dy);
PropagateMouseMove();
XOnMouseMoved(localx, localy);
for (size_t i = 0; i < children.size(); ++i)
{
if(children[i]->Enabled)
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy);
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
}
}
void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy)
void Panel::PropagateMouseMove()
{
mouseInside = true;
auto localx = ui::Engine::Ref().GetMouseX() - GetScreenPos().X;
auto localy = ui::Engine::Ref().GetMouseY() - GetScreenPos().Y;
for (size_t i = 0; i < children.size(); ++i)
{
if (children[i]->Enabled)
{
Point local (localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y)
, prevlocal (local.X - dx, local.Y - dy);
Point local (localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
// mouse currently inside?
if( local.X >= 0 &&
@ -196,14 +197,12 @@ void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy)
local.X < children[i]->Size.X &&
local.Y < children[i]->Size.Y )
{
children[i]->OnMouseMovedInside(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, dx, dy);
children[i]->OnMouseMoved(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y);
// was the mouse outside?
if(!(prevlocal.X >= 0 &&
prevlocal.Y >= 0 &&
prevlocal.X < children[i]->Size.X &&
prevlocal.Y < children[i]->Size.Y ) )
if (!children[i]->MouseInside)
{
children[i]->MouseInside = true;
children[i]->OnMouseEnter(local.X, local.Y);
}
}
@ -211,71 +210,59 @@ void Panel::OnMouseMovedInside(int localx, int localy, int dx, int dy)
else
{
// was the mouse inside?
if( prevlocal.X >= 0 &&
prevlocal.Y >= 0 &&
prevlocal.X < children[i]->Size.X &&
prevlocal.Y < children[i]->Size.Y )
if (children[i]->MouseInside)
{
children[i]->MouseInside = false;
children[i]->OnMouseLeave(local.X, local.Y);
}
}
}
}
// always allow hover on parent (?)
XOnMouseMovedInside(localx, localy, dx, dy);
}
void Panel::OnMouseEnter(int localx, int localy)
{
mouseInside = true;
XOnMouseEnter(localx, localy);
}
void Panel::OnMouseLeave(int localx, int localy)
{
mouseInside = false;
XOnMouseLeave(localx, localy);
}
void Panel::OnMouseUnclick(int localx, int localy, unsigned button)
{
bool childunclicked = false;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be unlocked
if(children[i]->Enabled)
{
//is mouse inside?
if( localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
childunclicked = true;
children[i]->OnMouseUnclick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
break;
}
}
}
//if a child wasn't clicked, send click to ourself
if (!childunclicked)
{
XOnMouseUnclick(localx, localy, button);
}
}
void Panel::OnMouseUp(int x, int y, unsigned button)
{
auto localx = x - Position.X;
auto localy = y - Position.Y;
//check if clicked a child
for(int i = children.size()-1; i >= 0 ; --i)
{
//child must be enabled
if(children[i]->Enabled)
{
//is mouse inside?
if( children[i]->MouseDownInside &&
localx >= children[i]->Position.X + ViewportPosition.X &&
localy >= children[i]->Position.Y + ViewportPosition.Y &&
localx < children[i]->Position.X + ViewportPosition.X + children[i]->Size.X &&
localy < children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y )
{
children[i]->OnMouseClick(localx - children[i]->Position.X - ViewportPosition.X, localy - children[i]->Position.Y - ViewportPosition.Y, button);
break;
}
}
}
for (auto *child : children)
{
child->MouseDownInside = false;
}
XOnMouseUp(x, y, button);
for (size_t i = 0; i < children.size(); ++i)
{
if (children[i]->Enabled)
children[i]->OnMouseUp(x, y, button);
children[i]->OnMouseUp(x - Position.X - ViewportPosition.X, y - Position.Y - ViewportPosition.Y, button);
}
}
@ -342,11 +329,7 @@ void Panel::XOnMouseHover(int localx, int localy)
{
}
void Panel::XOnMouseMoved(int localx, int localy, int dx, int dy)
{
}
void Panel::XOnMouseMovedInside(int localx, int localy, int dx, int dy)
void Panel::XOnMouseMoved(int localx, int localy)
{
}
@ -358,10 +341,6 @@ void Panel::XOnMouseLeave(int localx, int localy)
{
}
void Panel::XOnMouseUnclick(int localx, int localy, unsigned button)
{
}
void Panel::XOnMouseUp(int x, int y, unsigned button)
{
}

View File

@ -51,14 +51,12 @@ namespace ui
void Draw(const Point& screenPos) override;
void OnMouseHover(int localx, int localy) override;
void OnMouseMoved(int localx, int localy, int dx, int dy) override;
void OnMouseMovedInside(int localx, int localy, int dx, int dy) override;
void OnMouseMoved(int localx, int localy) override;
void OnMouseEnter(int localx, int localy) override;
void OnMouseLeave(int localx, int localy) override;
void OnMouseDown(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void OnMouseClick(int localx, int localy, unsigned button) override;
void OnMouseUnclick(int localx, int localy, unsigned button) override;
void OnMouseWheel(int localx, int localy, int d) override;
void OnMouseWheelInside(int localx, int localy, int d) override;
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
@ -67,7 +65,6 @@ namespace ui
protected:
// child components
std::vector<ui::Component*> children;
bool mouseInside;
// Overridable. Called by XComponent::Tick()
virtual void XTick(float dt);
@ -80,10 +77,7 @@ namespace ui
virtual void XOnMouseHover(int localx, int localy);
// Overridable. Called by XComponent::OnMouseMoved()
virtual void XOnMouseMoved(int localx, int localy, int dx, int dy);
// Overridable. Called by XComponent::OnMouseMovedInside()
virtual void XOnMouseMovedInside(int localx, int localy, int dx, int dy);
virtual void XOnMouseMoved(int localx, int localy);
// Overridable. Called by XComponent::OnMouseEnter()
virtual void XOnMouseEnter(int localx, int localy);
@ -100,9 +94,6 @@ namespace ui
// Overridable. Called by XComponent::OnMouseClick()
virtual void XOnMouseClick(int localx, int localy, unsigned button);
// Overridable. Called by XComponent::OnMouseUnclick()
virtual void XOnMouseUnclick(int localx, int localy, unsigned button);
// Overridable. Called by XComponent::OnMouseWheel()
virtual void XOnMouseWheel(int localx, int localy, int d);
@ -114,6 +105,8 @@ namespace ui
// Overridable. Called by XComponent::OnKeyRelease()
virtual void XOnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);
void PropagateMouseMove();
};
}

View File

@ -64,19 +64,22 @@ void RichLabel::SetText(String newText)
regions = newRegions;
}
void RichLabel::OnMouseClick(int x, int y, unsigned button)
void RichLabel::OnMouseDown(int x, int y, unsigned button)
{
int cursorPosition = displayTextWrapper.Point2Index(x - textPosition.X, y - textPosition.Y).raw_index;
for (auto const &region : regions)
if (MouseDownInside)
{
if (region.begin <= cursorPosition && region.end > cursorPosition)
int cursorPosition = displayTextWrapper.Point2Index(x - Position.X - textPosition.X, y - Position.Y - textPosition.Y).raw_index;
for (auto const &region : regions)
{
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(&region.action))
if (region.begin <= cursorPosition && region.end > cursorPosition)
{
Platform::OpenURI(linkAction->uri);
return;
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(&region.action))
{
Platform::OpenURI(linkAction->uri);
return;
}
}
}
}
Label::OnMouseClick(x, y, button);
Label::OnMouseDown(x, y, button);
}

View File

@ -24,6 +24,6 @@ namespace ui
RichLabel(Point position, Point size, String text);
void SetText(String newText) override;
void OnMouseClick(int x, int y, unsigned button) override;
void OnMouseDown(int x, int y, unsigned button) override;
};
}

View File

@ -262,7 +262,7 @@ void SaveButton::Draw(const Point& screenPos)
}
}
void SaveButton::OnMouseUnclick(int x, int y, unsigned int button)
void SaveButton::OnMouseClick(int x, int y, unsigned int button)
{
if(button != 1)
{
@ -333,36 +333,40 @@ void SaveButton::OnContextMenuAction(int item)
}
}
void SaveButton::OnMouseClick(int x, int y, unsigned int button)
void SaveButton::OnMouseDown(int x, int y, unsigned int button)
{
if(button == SDL_BUTTON_RIGHT)
if (MouseDownInside)
{
if(menu)
menu->Show(GetScreenPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
if(button !=1 && selectable)
if(button == SDL_BUTTON_RIGHT)
{
selected = !selected;
DoSelection();
if(menu)
menu->Show(GetContainerPos() + ui::Point(x, y));
}
else
{
isButtonDown = true;
if(button !=1 && selectable)
{
selected = !selected;
DoSelection();
}
}
}
}
void SaveButton::OnMouseMovedInside(int x, int y, int dx, int dy)
void SaveButton::OnMouseMoved(int x, int y)
{
if(y > Size.Y-11)
isMouseInsideAuthor = true;
else
isMouseInsideAuthor = false;
isMouseInsideAuthor = false;
isMouseInsideHistory = false;
if (MouseInside)
{
if (y > Size.Y-11)
isMouseInsideAuthor = true;
if(showVotes && y > Size.Y-29 && y < Size.Y - 18 && x > 0 && x < 9)
isMouseInsideHistory = true;
else
isMouseInsideHistory = false;
if (y > Size.Y-29 && y < Size.Y - 18 && x > 0 && x < 9)
isMouseInsideHistory = true;
}
}
void SaveButton::OnMouseEnter(int x, int y)

View File

@ -48,12 +48,12 @@ public:
virtual ~SaveButton();
void OnMouseClick(int x, int y, unsigned int button) override;
void OnMouseUnclick(int x, int y, unsigned int button) override;
void OnMouseDown(int x, int y, unsigned int button) override;
void OnMouseEnter(int x, int y) override;
void OnMouseLeave(int x, int y) override;
void OnMouseMovedInside(int x, int y, int dx, int dy) override;
void OnMouseMoved(int x, int y) override;
void AddContextMenu(int menuType);
void OnContextMenuAction(int item) override;

View File

@ -1,8 +1,9 @@
#include "ScrollPanel.h"
#include "Engine.h"
#include "graphics/Graphics.h"
#include "Misc.h"
#include "PowderToySDL.h"
#include "Window.h"
#include <algorithm>
using namespace ui;
@ -70,28 +71,56 @@ void ScrollPanel::Draw(const Point& screenPos)
}
}
void ScrollPanel::XOnMouseClick(int x, int y, unsigned int button)
void ScrollPanel::XOnMouseDown(int x, int y, unsigned int button)
{
if (isMouseInsideScrollbar)
if (MouseDownInside)
{
scrollbarSelected = true;
scrollbarInitialYOffset = int(offsetY);
CancelPanning();
if (isMouseInsideScrollbar)
{
scrollbarSelected = true;
scrollbarInitialYOffset = int(offsetY);
}
initialOffsetY = offsetY;
scrollbarInitialYClick = y - Position.Y;
scrollbarClickLocation = 100;
}
scrollbarInitialYClick = y;
scrollbarClickLocation = 100;
}
void ScrollPanel::CancelPanning()
{
panning = false;
panHistory = {};
yScrollVel = 0;
}
void ScrollPanel::XOnMouseUp(int x, int y, unsigned int button)
{
scrollbarSelected = false;
auto oldPanHistory = panHistory;
CancelPanning();
{
auto it = oldPanHistory.end();
while (it != oldPanHistory.begin() && *(it - 1))
{
--it;
}
if (it < oldPanHistory.end())
{
auto offsetYDiff = oldPanHistory.back()->offsetY - (*it)->offsetY;
auto tickDiff = oldPanHistory.back()->ticks - (*it)->ticks;
yScrollVel += offsetYDiff / tickDiff * (1000.f / Engine::Ref().GetFps());
}
}
isMouseInsideScrollbarArea = false;
scrollbarClickLocation = 0;
}
void ScrollPanel::XOnMouseMoved(int x, int y, int dx, int dy)
void ScrollPanel::XOnMouseMoved(int x, int y)
{
if(maxOffset.Y>0 && InnerSize.Y>0)
{
auto oldViewportPositionY = ViewportPosition.Y;
float scrollHeight = float(Size.Y)*(float(Size.Y)/float(InnerSize.Y));
float scrollPos = 0;
if (-ViewportPosition.Y>0)
@ -113,6 +142,19 @@ void ScrollPanel::XOnMouseMoved(int x, int y, int dx, int dy)
offsetY = float(scrollbarInitialYOffset);
}
}
else if (MouseDownInside)
{
Vec2<int> mouseAt{ x, y };
if (Engine::Ref().TouchUI && iabs(scrollbarInitialYClick - mouseAt.Y) > PanOffsetThreshold)
{
panning = true;
for (auto *child : children)
{
child->MouseDownInside = false;
}
GetParentWindow()->FocusComponent(NULL);
}
}
if (x > (Size.X-scrollBarWidth) && x < (Size.X-scrollBarWidth)+scrollBarWidth)
{
@ -122,11 +164,31 @@ void ScrollPanel::XOnMouseMoved(int x, int y, int dx, int dy)
}
else
isMouseInsideScrollbar = false;
if (oldViewportPositionY != ViewportPosition.Y)
{
PropagateMouseMove();
}
}
}
void ScrollPanel::XTick(float dt)
{
auto oldViewportPositionY = ViewportPosition.Y;
if (panning)
{
auto scrollY = initialOffsetY + scrollbarInitialYClick - (Engine::Ref().GetMouseY() - GetScreenPos().Y);
ViewportPosition.Y = -scrollY;
offsetY = float(scrollY);
PanPoint p{ offsetY, GetTicks() };
if (!(panHistory.back() && panHistory.back()->ticks == p.ticks))
{
std::copy(panHistory.begin() + 1, panHistory.end(), panHistory.begin());
panHistory.back() = p;
}
}
if (xScrollVel > 7.0f) xScrollVel = 7.0f;
if (xScrollVel < -7.0f) xScrollVel = -7.0f;
if (xScrollVel > -0.5f && xScrollVel < 0.5)
@ -183,9 +245,9 @@ void ScrollPanel::XTick(float dt)
}
}
if (mouseInside && scrollBarWidth < 6)
if (MouseInside && scrollBarWidth < 6)
scrollBarWidth++;
else if (!mouseInside && scrollBarWidth > 0 && !scrollbarSelected)
else if (!MouseInside && scrollBarWidth > 0 && !scrollbarSelected)
scrollBarWidth--;
if (isMouseInsideScrollbarArea && scrollbarClickLocation && !scrollbarSelected)
@ -205,4 +267,9 @@ void ScrollPanel::XTick(float dt)
offsetY += scrollbarClickLocation*scrollHeight/10;
ViewportPosition.Y -= int(scrollbarClickLocation*scrollHeight/10);
}
if (oldViewportPositionY != ViewportPosition.Y)
{
PropagateMouseMove();
}
}

View File

@ -1,11 +1,14 @@
#pragma once
#include "Panel.h"
#include <optional>
#include <array>
namespace ui
{
class ScrollPanel: public Panel
{
void CancelPanning();
protected:
int scrollBarWidth;
Point maxOffset;
@ -19,6 +22,16 @@ namespace ui
int scrollbarInitialYOffset;
int scrollbarInitialYClick;
int scrollbarClickLocation;
int initialOffsetY;
bool panning = false;
static constexpr int PanOffsetThreshold = 10;
static constexpr int PanHistorySize = 5;
struct PanPoint
{
float offsetY;
unsigned int ticks;
};
std::array<std::optional<PanPoint>, PanHistorySize> panHistory;
public:
ScrollPanel(Point position, Point size);
@ -28,8 +41,8 @@ namespace ui
void Draw(const Point& screenPos) override;
void XTick(float dt) override;
void XOnMouseWheelInside(int localx, int localy, int d) override;
void XOnMouseClick(int localx, int localy, unsigned int button) override;
void XOnMouseDown(int localx, int localy, unsigned int button) override;
void XOnMouseUp(int x, int y, unsigned int button) override;
void XOnMouseMoved(int localx, int localy, int dx, int dy) override;
void XOnMouseMoved(int localx, int localy) override;
};
}

View File

@ -40,7 +40,7 @@ void Slider::updatePosition(int position)
}
}
void Slider::OnMouseMoved(int x, int y, int dx, int dy)
void Slider::OnMouseMoved(int x, int y)
{
if(isMouseDown)
{
@ -48,10 +48,13 @@ void Slider::OnMouseMoved(int x, int y, int dx, int dy)
}
}
void Slider::OnMouseClick(int x, int y, unsigned button)
void Slider::OnMouseDown(int x, int y, unsigned button)
{
isMouseDown = true;
updatePosition(x);
if (MouseDownInside)
{
isMouseDown = true;
updatePosition(x - Position.X);
}
}
void Slider::OnMouseUp(int x, int y, unsigned button)

View File

@ -24,8 +24,8 @@ public:
Slider(Point position, Point size, int steps);
virtual ~Slider() = default;
void OnMouseMoved(int x, int y, int dx, int dy) override;
void OnMouseClick(int x, int y, unsigned button) override;
void OnMouseMoved(int x, int y) override;
void OnMouseDown(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void Draw(const Point& screenPos) override;
void SetColour(Colour col1, Colour col2);

View File

@ -569,18 +569,21 @@ void Textbox::OnTextEditing(String text)
updateSelection();
}
void Textbox::OnMouseClick(int x, int y, unsigned button)
void Textbox::OnMouseDown(int x, int y, unsigned button)
{
if (button != SDL_BUTTON_RIGHT)
if (MouseDownInside)
{
StopTextEditing();
mouseDown = true;
auto tp = textPosition - Vec2{ scrollX, 0 };
auto index = textWrapper.Point2Index(x-tp.X, y-tp.Y);
cursor = index.raw_index;
resetCursorPosition();
if (button != SDL_BUTTON_RIGHT)
{
StopTextEditing();
mouseDown = true;
auto tp = textPosition - Vec2{ scrollX, 0 };
auto index = textWrapper.Point2Index(x-Position.X-tp.X, y-Position.Y-tp.Y);
cursor = index.raw_index;
resetCursorPosition();
}
}
Label::OnMouseClick(x, y, button);
Label::OnMouseDown(x, y, button);
}
void Textbox::OnMouseUp(int x, int y, unsigned button)
@ -589,7 +592,7 @@ void Textbox::OnMouseUp(int x, int y, unsigned button)
Label::OnMouseUp(x, y, button);
}
void Textbox::OnMouseMoved(int localx, int localy, int dx, int dy)
void Textbox::OnMouseMoved(int localx, int localy)
{
if(mouseDown)
{
@ -598,7 +601,7 @@ void Textbox::OnMouseMoved(int localx, int localy, int dx, int dy)
cursor = index.raw_index;
resetCursorPosition();
}
Label::OnMouseMoved(localx, localy, dx, dy);
Label::OnMouseMoved(localx, localy);
}
void Textbox::OnDefocus()

View File

@ -53,9 +53,9 @@ public:
void Tick(float dt) override;
void OnContextMenuAction(int item) override;
void OnMouseClick(int x, int y, unsigned button) override;
void OnMouseDown(int x, int y, unsigned button) override;
void OnMouseUp(int x, int y, unsigned button) override;
void OnMouseMoved(int localx, int localy, int dx, int dy) override;
void OnMouseMoved(int localx, int localy) override;
void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;
void OnVKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt);
void OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override;

View File

@ -43,11 +43,16 @@ void Window::AddComponent(Component* c)
if (c->GetParentWindow() == NULL)
{
c->SetParentWindow(this);
c->MouseInside = false;
c->MouseDownInside = false;
Components.push_back(c);
if (Engine::Ref().GetMouseX() > Position.X + c->Position.X && Engine::Ref().GetMouseX() < Position.X + c->Position.X + c->Size.X &&
Engine::Ref().GetMouseY() > Position.Y + c->Position.Y && Engine::Ref().GetMouseY() < Position.Y + c->Position.Y + c->Size.Y)
{
c->MouseInside = true;
c->OnMouseEnter(Engine::Ref().GetMouseX() - (Position.X + c->Position.X), Engine::Ref().GetMouseY() - (Position.Y + c->Position.Y));
}
}
else
{
@ -440,7 +445,7 @@ void Window::DoMouseDown(int x_, int y_, unsigned button)
FocusComponent(Components[i]);
if (!DEBUG || !debugMode)
{
Components[i]->OnMouseClick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button);
Components[i]->MouseDownInside = true;
}
clickState = true;
break;
@ -485,21 +490,17 @@ void Window::DoMouseMove(int x_, int y_, int dx, int dy)
Point local(x - Components[i]->Position.X, y - Components[i]->Position.Y);
Point a(local.X - dx, local.Y - dy);
Components[i]->OnMouseMoved(local.X, local.Y, dx, dy);
Components[i]->OnMouseMoved(local.X, local.Y);
if (local.X >= 0 &&
local.Y >= 0 &&
local.X < Components[i]->Size.X &&
local.Y < Components[i]->Size.Y && !halt)
{
Components[i]->OnMouseMovedInside(local.X, local.Y, dx, dy);
// entering?
if (!(a.X >= 0 &&
a.Y >= 0 &&
a.X < Components[i]->Size.X &&
a.Y < Components[i]->Size.Y ))
if (!Components[i]->MouseInside)
{
Components[i]->MouseInside = true;
Components[i]->OnMouseEnter(local.X, local.Y);
}
if (Components[i]->Enabled)
@ -508,11 +509,9 @@ void Window::DoMouseMove(int x_, int y_, int dx, int dy)
else if (!halt)
{
// leaving?
if (a.X >= 0 &&
a.Y >= 0 &&
a.X < Components[i]->Size.X &&
a.Y < Components[i]->Size.Y )
if (Components[i]->MouseInside)
{
Components[i]->MouseInside = false;
Components[i]->OnMouseLeave(local.X, local.Y);
}
@ -537,13 +536,17 @@ void Window::DoMouseUp(int x_, int y_, unsigned button)
{
if (Components[i]->Enabled && Components[i]->Visible)
{
if (x >= Components[i]->Position.X && y >= Components[i]->Position.Y && x < Components[i]->Position.X + Components[i]->Size.X && y < Components[i]->Position.Y + Components[i]->Size.Y)
if (Components[i]->MouseDownInside && x >= Components[i]->Position.X && y >= Components[i]->Position.Y && x < Components[i]->Position.X + Components[i]->Size.X && y < Components[i]->Position.Y + Components[i]->Size.Y)
{
Components[i]->OnMouseUnclick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button);
Components[i]->OnMouseClick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button);
break;
}
}
}
for (auto *component : Components)
{
component->MouseDownInside = false;
}
//on mouse up
for (int i = Components.size() - 1; i >= 0 && !halt; --i)

View File

@ -59,6 +59,8 @@ void LocalBrowserModel::OpenSave(int index)
{
stamp = std::move(savesList[index]);
savesList.clear();
notifyPageChanged();
notifySavesListChanged();
}
bool LocalBrowserModel::GetMoveToFront()

View File

@ -1,5 +1,6 @@
#include "LoginModel.h"
#include "LoginView.h"
#include "Config.h"
#include "client/Client.h"
#include "client/http/LoginRequest.h"
#include "client/http/LogoutRequest.h"
@ -8,7 +9,7 @@ void LoginModel::Login(ByteString username, ByteString password)
{
if (username.Contains("@"))
{
statusText = "Use your Powder Toy account to log in, not your email. If you don't have a Powder Toy account, you can create one at https://powdertoy.co.uk/Register.html";
statusText = String::Build("Use your Powder Toy account to log in, not your email. If you don't have a Powder Toy account, you can create one at https://", SERVER, "/Register.html");
loginStatus = loginIdle;
notifyStatusChanged();
return;

View File

@ -8,6 +8,7 @@
#include "graphics/Renderer.h"
#include "gui/Style.h"
#include "simulation/ElementDefs.h"
#include "simulation/SimulationData.h"
#include "client/Client.h"
#include "gui/dialogues/ConfirmPrompt.h"
#include "gui/dialogues/InformationMessage.h"
@ -75,13 +76,13 @@ OptionsView::OptionsView() : ui::Window(ui::Point(-1, -1), ui::Point(320, 340))
auto *checkbox = new ui::Checkbox(ui::Point(8 + indent * 15, currentY), ui::Point(1, 16), text, "");
autoWidth(checkbox, 0);
checkbox->SetActionCallback({ action });
scrollPanel->AddChild(checkbox);
currentY += 14;
if (info.size())
{
addLabel(indent, info);
}
currentY += 4;
scrollPanel->AddChild(checkbox);
return checkbox;
};
auto addDropDown = [this, &currentY, &autoWidth](String info, std::vector<std::pair<String, int>> options, std::function<void ()> action) {
@ -120,11 +121,11 @@ OptionsView::OptionsView() : ui::Window(ui::Point(-1, -1), ui::Point(320, 340))
c->SetWaterEqualisation(waterEqualisation->GetChecked());
});
airMode = addDropDown("Air simulation mode", {
{ "On", 0 },
{ "Pressure off", 1 },
{ "Velocity off", 2 },
{ "Off", 3 },
{ "No update", 4 },
{ "On", AIR_ON },
{ "Pressure off", AIR_PRESSUREOFF },
{ "Velocity off", AIR_VELOCITYOFF },
{ "Off", AIR_OFF },
{ "No update", AIR_NOUPDATE },
}, [this] {
c->SetAirMode(airMode->GetOption().second);
});
@ -136,10 +137,11 @@ OptionsView::OptionsView() : ui::Window(ui::Point(-1, -1), ui::Point(320, 340))
ambientAirTemp->SetDefocusCallback({ [this] {
UpdateAirTemp(ambientAirTemp->GetText(), true);
}});
ambientAirTemp->SetLimit(9);
scrollPanel->AddChild(ambientAirTemp);
ambientAirTempPreview = new ui::Button(ui::Point(Size.X-31, currentY), ui::Point(16, 16), "", "Preview");
scrollPanel->AddChild(ambientAirTempPreview);
auto *label = new ui::Label(ui::Point(8, currentY), ui::Point(Size.X-96, 16), "Ambient air temperature");
auto *label = new ui::Label(ui::Point(8, currentY), ui::Point(Size.X-105, 16), "Ambient air temperature");
label->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
label->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
scrollPanel->AddChild(label);
@ -210,10 +212,10 @@ OptionsView::OptionsView() : ui::Window(ui::Point(-1, -1), ui::Point(320, 340))
}
};
gravityMode = addDropDown("Gravity simulation mode", {
{ "Vertical", 0 },
{ "Off", 1 },
{ "Radial", 2 },
{ "Custom", 3 },
{ "Vertical", GRAV_VERTICAL },
{ "Off", GRAV_OFF },
{ "Radial", GRAV_RADIAL },
{ "Custom", GRAV_CUSTOM },
}, [this] {
c->SetGravityMode(gravityMode->GetOption().second);
if (gravityMode->GetOption().second == 3)
@ -222,9 +224,9 @@ OptionsView::OptionsView() : ui::Window(ui::Point(-1, -1), ui::Point(320, 340))
}
});
edgeMode = addDropDown("Edge mode", {
{ "Void", 0 },
{ "Solid", 1 },
{ "Loop", 2 },
{ "Void", EDGE_VOID },
{ "Solid", EDGE_SOLID },
{ "Loop", EDGE_LOOP },
}, [this] {
c->SetEdgeMode(edgeMode->GetOption().second);
});
@ -317,10 +319,10 @@ OptionsView::OptionsView() : ui::Window(ui::Point(-1, -1), ui::Point(320, 340))
currentY += 4; // and then undo the undo
}
decoSpace = addDropDown("Colour space used by decoration tools", {
{ "sRGB", 0 },
{ "Linear", 1 },
{ "Gamma 2.2", 2 },
{ "Gamma 1.8", 3 },
{ "sRGB", DECOSPACE_SRGB },
{ "Linear", DECOSPACE_LINEAR },
{ "Gamma 2.2", DECOSPACE_GAMMA22 },
{ "Gamma 1.8", DECOSPACE_GAMMA18 },
}, [this] {
c->SetDecoSpace(decoSpace->GetOption().second);
});

View File

@ -147,7 +147,7 @@ void PreviewModel::OnSaveReady()
{
auto gameSave = std::make_unique<GameSave>(*saveData);
if (gameSave->fromNewerVersion)
new ErrorMessage("This save is from a newer version", "Please update TPT in game or at https://powdertoy.co.uk");
new ErrorMessage("This save is from a newer version", String::Build("Please update TPT in game or at https://", SERVER));
saveInfo->SetGameSave(std::move(gameSave));
}
catch(ParseException &e)

View File

@ -111,9 +111,9 @@ PreviewView::PreviewView(std::unique_ptr<VideoBuffer> newSavePreview):
AddComponent(missingElementsButton);
if(showAvatars)
saveNameLabel = new ui::Label(ui::Point(39, (YRES/2)+4), ui::Point(180, 16), "");
saveNameLabel = new ui::Label(ui::Point(39, (YRES/2)+4), ui::Point(265, 16), "");
else
saveNameLabel = new ui::Label(ui::Point(5, (YRES/2)+4), ui::Point(200, 16), "");
saveNameLabel = new ui::Label(ui::Point(5, (YRES/2)+4), ui::Point(300, 16), "");
saveNameLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
saveNameLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(saveNameLabel);
@ -129,9 +129,9 @@ PreviewView::PreviewView(std::unique_ptr<VideoBuffer> newSavePreview):
AddComponent(saveDescriptionLabel);
if(showAvatars)
authorDateLabel = new ui::Label(ui::Point(39, (YRES/2)+4+15), ui::Point(180, 16), "");
authorDateLabel = new ui::Label(ui::Point(39, (YRES/2)+4+15), ui::Point(200, 16), "");
else
authorDateLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15), ui::Point(200, 16), "");
authorDateLabel = new ui::Label(ui::Point(5, (YRES/2)+4+15), ui::Point(220, 16), "");
authorDateLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
authorDateLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(authorDateLabel);
@ -148,7 +148,7 @@ PreviewView::PreviewView(std::unique_ptr<VideoBuffer> newSavePreview):
AddComponent(avatarButton);
}
viewsLabel = new ui::Label(ui::Point((XRES/2)-80, (YRES/2)+4+15), ui::Point(80, 16), "");
viewsLabel = new ui::Label(ui::Point((XRES/2)-88, (YRES/2)+4+15), ui::Point(88, 16), "");
viewsLabel->Appearance.HorizontalAlign = ui::Appearance::AlignRight;
viewsLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(viewsLabel);
@ -644,6 +644,7 @@ void PreviewView::NotifyCommentBoxEnabledChanged(PreviewModel * sender)
} });
addCommentBox->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
addCommentBox->SetMultiline(true);
addCommentBox->SetLimit(1000);
AddComponent(addCommentBox);
submitCommentButton = new ui::Button(ui::Point(Size.X-40, Size.Y-19), ui::Point(40, 19), "Submit");
submitCommentButton->SetActionCallback({ [this] { submitComment(); } });

View File

@ -70,7 +70,7 @@ void ProfileActivity::setUserInfo(UserInfo newInfo)
int currentY = 5;
// username label
ui::Label * title = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8-(40+8+75), 15), info.username.FromUtf8());
ui::Label * title = new ui::Label(ui::Point(4, currentY), ui::Point(Size.X-8-(40+16+75), 15), info.username.FromUtf8());
title->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
scrollPanel->AddChild(title);
@ -90,13 +90,13 @@ void ProfileActivity::setUserInfo(UserInfo newInfo)
currentY += 23;
// age
ui::Label * ageTitle = new ui::Label(ui::Point(4, currentY), ui::Point(18, 15), "Age:");
ui::Label * ageTitle = new ui::Label(ui::Point(4, currentY), ui::Point(23, 15), "Age:");
ageTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
ageTitle->SetTextColour(ui::Colour(180, 180, 180));
scrollPanel->AddChild(ageTitle);
// can't figure out how to tell a null from a 0 in the json library we use
ui::Label *age = new ui::Label(ui::Point(8+ageTitle->Size.X, currentY), ui::Point(40, 15), info.age ? String::Build(info.age) : "\bgNot Provided");
ui::Label *age = new ui::Label(ui::Point(5+ageTitle->Size.X, currentY), ui::Point(Size.X-ageTitle->Size.X-56, 15), info.age ? String::Build(info.age) : "\bgNot Provided");
age->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
scrollPanel->AddChild(age);
currentY += 2+age->Size.Y;
@ -108,20 +108,25 @@ void ProfileActivity::setUserInfo(UserInfo newInfo)
scrollPanel->AddChild(locationTitle);
if (editable)
location = new ui::Textbox(ui::Point(8+locationTitle->Size.X, currentY), ui::Point(Size.X-locationTitle->Size.X-16, 17), info.location);
{
location = new ui::Textbox(ui::Point(5+locationTitle->Size.X, currentY), ui::Point(Size.X-locationTitle->Size.X-16, 17), info.location);
((ui::Textbox*)location)->SetLimit(40);
}
else
location = new ui::Label(ui::Point(4+locationTitle->Size.X, currentY), ui::Point(Size.X-locationTitle->Size.X-14, 17), info.location);
{
location = new ui::Label(ui::Point(5+locationTitle->Size.X, currentY), ui::Point(Size.X-locationTitle->Size.X-14, 17), info.location);
}
location->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
scrollPanel->AddChild(location);
currentY += 2+location->Size.Y;
// website
ui::Label * websiteTitle = new ui::Label(ui::Point(4, currentY), ui::Point(38, 15), "Website:");
ui::Label * websiteTitle = new ui::Label(ui::Point(1, currentY), ui::Point(42, 15), "Website:");
websiteTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
websiteTitle->SetTextColour(ui::Colour(180, 180, 180));
scrollPanel->AddChild(websiteTitle);
ui::Label *website = new ui::Label(ui::Point(8+websiteTitle->Size.X, currentY), ui::Point(Size.X-websiteTitle->Size.X-16, 15), info.website.FromUtf8());
ui::Label *website = new ui::Label(ui::Point(2+websiteTitle->Size.X, currentY), ui::Point(Size.X-websiteTitle->Size.X-16, 15), info.website.FromUtf8());
website->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
scrollPanel->AddChild(website);
currentY += 2+website->Size.Y;
@ -134,34 +139,34 @@ void ProfileActivity::setUserInfo(UserInfo newInfo)
currentY += savesTitle->Size.Y;
// saves count
ui::Label * saveCountTitle = new ui::Label(ui::Point(12, currentY), ui::Point(30, 15), "Count:");
ui::Label * saveCountTitle = new ui::Label(ui::Point(12, currentY), ui::Point(32, 15), "Count:");
saveCountTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
saveCountTitle->SetTextColour(ui::Colour(180, 180, 180));
scrollPanel->AddChild(saveCountTitle);
ui::Label *savesCount = new ui::Label(ui::Point(12+saveCountTitle->Size.X, currentY), ui::Point(Size.X-saveCountTitle->Size.X-16, 15), String::Build(info.saveCount));
ui::Label *savesCount = new ui::Label(ui::Point(13+saveCountTitle->Size.X, currentY), ui::Point(Size.X-saveCountTitle->Size.X-24, 15), String::Build(info.saveCount));
savesCount->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
scrollPanel->AddChild(savesCount);
currentY += savesCount->Size.Y;
// average score
ui::Label * averageScoreTitle = new ui::Label(ui::Point(12, currentY), ui::Point(70, 15), "Average Score:");
ui::Label * averageScoreTitle = new ui::Label(ui::Point(12, currentY), ui::Point(72, 15), "Average Score:");
averageScoreTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
averageScoreTitle->SetTextColour(ui::Colour(180, 180, 180));
scrollPanel->AddChild(averageScoreTitle);
ui::Label *averageScore = new ui::Label(ui::Point(12+averageScoreTitle->Size.X, currentY), ui::Point(Size.X-averageScoreTitle->Size.X-16, 15), String::Build(info.averageScore));
ui::Label *averageScore = new ui::Label(ui::Point(13+averageScoreTitle->Size.X, currentY), ui::Point(Size.X-averageScoreTitle->Size.X-24, 15), String::Build(info.averageScore));
averageScore->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
scrollPanel->AddChild(averageScore);
currentY += averageScore->Size.Y;
// highest score
ui::Label * highestScoreTitle = new ui::Label(ui::Point(12, currentY), ui::Point(69, 15), "Highest Score:");
ui::Label * highestScoreTitle = new ui::Label(ui::Point(12, currentY), ui::Point(71, 15), "Highest Score:");
highestScoreTitle->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
highestScoreTitle->SetTextColour(ui::Colour(180, 180, 180));
scrollPanel->AddChild(highestScoreTitle);
ui::Label *highestScore = new ui::Label(ui::Point(12+highestScoreTitle->Size.X, currentY), ui::Point(Size.X-highestScoreTitle->Size.X-16, 15), String::Build(info.highestScore));
ui::Label *highestScore = new ui::Label(ui::Point(13+highestScoreTitle->Size.X, currentY), ui::Point(Size.X-highestScoreTitle->Size.X-24, 15), String::Build(info.highestScore));
highestScore->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
scrollPanel->AddChild(highestScore);
currentY += 2+highestScore->Size.Y;

View File

@ -79,6 +79,7 @@ ServerSaveActivity::ServerSaveActivity(std::unique_ptr<SaveInfo> newSave, OnUplo
nameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
nameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
nameField->SetActionCallback({ [this] { CheckName(nameField->GetText()); } });
nameField->SetLimit(50);
AddComponent(nameField);
FocusComponent(nameField);

View File

@ -136,6 +136,32 @@ void SearchController::SetPageRelative(int offset)
searchModel->UpdateSaveList(page, searchModel->GetLastQuery());
}
void SearchController::ChangePeriod(int period)
{
switch(period)
{
case 0:
searchModel->SetPeriod(http::allSaves);
break;
case 1:
searchModel->SetPeriod(http::todaySaves);
break;
case 2:
searchModel->SetPeriod(http::weekSaves);
break;
case 3:
searchModel->SetPeriod(http::monthSaves);
break;
case 4:
searchModel->SetPeriod(http::yearSaves);
break;
default:
searchModel->SetPeriod(http::allSaves);
}
searchModel->UpdateSaveList(1, searchModel->GetLastQuery());
}
void SearchController::ChangeSort()
{
if(searchModel->GetSort() == http::sortByDate)

View File

@ -37,6 +37,7 @@ public:
void Refresh();
void SetPage(int page);
void SetPageRelative(int offset);
void ChangePeriod(int period);
void ChangeSort();
void ShowOwn(bool show);
void ShowFavourite(bool show);

View File

@ -11,6 +11,7 @@
#include <cmath>
SearchModel::SearchModel():
currentPeriod(http::allSaves),
currentSort(http::sortByVotes),
currentPage(1),
resultCount(0),
@ -30,11 +31,11 @@ bool SearchModel::GetShowTags()
return showTags;
}
void SearchModel::BeginSearchSaves(int start, int count, String query, http::Sort sort, http::Category category)
void SearchModel::BeginSearchSaves(int start, int count, String query, http::Period period, http::Sort sort, http::Category category)
{
lastError = "";
resultCount = 0;
searchSaves = std::make_unique<http::SearchSavesRequest>(start, count, query.ToUtf8(), sort, category);
searchSaves = std::make_unique<http::SearchSavesRequest>(start, count, query.ToUtf8(), period, sort, category);
searchSaves->Start();
}
@ -95,7 +96,7 @@ bool SearchModel::UpdateSaveList(int pageNumber, String query)
//resultCount = 0;
currentPage = pageNumber;
if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == http::sortByVotes && query == "")
if(pageNumber == 1 && !showOwn && !showFavourite && currentPeriod == http::allSaves && currentSort == http::sortByVotes && query == "")
SetShowTags(true);
else
SetShowTags(false);
@ -120,7 +121,7 @@ bool SearchModel::UpdateSaveList(int pageNumber, String query)
{
category = http::categoryMyOwn;
}
BeginSearchSaves((currentPage-1)*20, 20, lastQuery, currentSort, category);
BeginSearchSaves((currentPage-1)*20, 20, lastQuery, currentPeriod, currentSort, category);
return true;
}
return false;
@ -178,6 +179,7 @@ void SearchModel::AddObserver(SearchView * observer)
observers.push_back(observer);
observer->NotifySaveListChanged(this);
observer->NotifyPageChanged(this);
observer->NotifyPeriodChanged(this);
observer->NotifySortChanged(this);
observer->NotifyShowOwnChanged(this);
observer->NotifyTagListChanged(this);
@ -258,6 +260,15 @@ void SearchModel::notifyPageChanged()
}
}
void SearchModel::notifyPeriodChanged()
{
for (size_t i = 0; i < observers.size(); i++)
{
SearchView* cObserver = observers[i];
cObserver->NotifyPeriodChanged(this);
}
}
void SearchModel::notifySortChanged()
{
for (size_t i = 0; i < observers.size(); i++)
@ -296,7 +307,7 @@ void SearchModel::notifySelectedChanged()
int SearchModel::GetPageCount()
{
if (!showOwn && !showFavourite && currentSort == http::sortByVotes && lastQuery == "")
if (!showOwn && !showFavourite && currentPeriod == http::allSaves && currentSort == http::sortByVotes && lastQuery == "")
return std::max(1, (int)(ceil(resultCount/20.0f))+1); //add one for front page (front page saves are repeated twice)
else
return std::max(1, (int)(ceil(resultCount/20.0f)));

View File

@ -18,7 +18,7 @@ class SearchModel
{
private:
std::unique_ptr<http::SearchSavesRequest> searchSaves;
void BeginSearchSaves(int start, int count, String query, http::Sort sort, http::Category category);
void BeginSearchSaves(int start, int count, String query, http::Period period, http::Sort sort, http::Category category);
std::vector<std::unique_ptr<SaveInfo>> EndSearchSaves();
void BeginGetTags(int start, int count, String query);
@ -26,6 +26,7 @@ private:
std::unique_ptr<http::SearchTagsRequest> getTags;
std::unique_ptr<SaveInfo> loadedSave;
http::Period currentPeriod;
http::Sort currentSort;
String lastQuery;
String lastError;
@ -42,6 +43,7 @@ private:
void notifyTagListChanged();
void notifySelectedChanged();
void notifyPageChanged();
void notifyPeriodChanged();
void notifySortChanged();
void notifyShowOwnChanged();
void notifyShowFavouriteChanged();
@ -61,6 +63,8 @@ public:
int GetPageCount();
int GetPageNum() { return currentPage; }
String GetLastQuery() { return lastQuery; }
void SetPeriod(http::Period period) { if(!searchSaves) { currentPeriod = period; } notifyPeriodChanged(); }
http::Period GetPeriod() { return currentPeriod; }
void SetSort(http::Sort sort) { if(!searchSaves) { currentSort = sort; } notifySortChanged(); }
http::Sort GetSort() { return currentSort; }
void SetShowOwn(bool show) { if(!searchSaves) { if(show!=showOwn) { showOwn = show; } } notifyShowOwnChanged(); }

View File

@ -9,6 +9,7 @@
#include "gui/interface/RichLabel.h"
#include "gui/interface/Textbox.h"
#include "gui/interface/Spinner.h"
#include "gui/interface/DropDown.h"
#include "PowderToySDL.h"
#include "graphics/Graphics.h"
#include "SimulationConfig.h"
@ -43,13 +44,25 @@ SearchView::SearchView():
AddComponent(pageCountLabel);
AddComponent(pageTextbox);
searchField = new ui::Textbox(ui::Point(60, 10), ui::Point(WINDOWW-238, 17), "", "[search]");
searchField = new ui::Textbox(ui::Point(60, 10), ui::Point(WINDOWW-283, 17), "", "[search]");
searchField->Appearance.icon = IconSearch;
searchField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
searchField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
searchField->SetActionCallback({ [this] { doSearch(); } });
searchField->SetLimit(100);
FocusComponent(searchField);
dateRange = new ui::DropDown(ui::Point(WINDOWW-185, 10), ui::Point(36, 17));
dateRange->SetActionCallback({ [this] { c->ChangePeriod(dateRange->GetOption().second); } });
dateRange->AddOption({"All", 0});
dateRange->AddOption({"Day", 1});
dateRange->AddOption({"Week", 2});
dateRange->AddOption({"Month", 3});
dateRange->AddOption({"Year", 4});
dateRange->Appearance.HorizontalAlign = ui::Appearance::AlignCentre;
dateRange->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
AddComponent(dateRange);
sortButton = new ui::Button(ui::Point(WINDOWW-140, 10), ui::Point(61, 17), "Sort");
sortButton->SetIcon(IconVoteSort);
sortButton->SetTogglable(true);
@ -201,6 +214,11 @@ void SearchView::Search(String query)
c->DoSearch(query, true);
}
void SearchView::NotifyPeriodChanged(SearchModel * sender)
{
dateRange->SetOption(sender->GetPeriod());
}
void SearchView::NotifySortChanged(SearchModel * sender)
{
if(sender->GetSort() == http::sortByVotes)
@ -489,7 +507,7 @@ void SearchView::NotifySaveListChanged(SearchModel * sender)
loadingSpinner->Visible = false;
if (!errorLabel)
{
errorLabel = new ui::Label(ui::Point((WINDOWW/2)-100, (WINDOWH/2)-6), ui::Point(200, 12), "Error");
errorLabel = new ui::Label(ui::Point(0, (WINDOWH/2)-6), ui::Point(WINDOWW, 12), "Error");
AddComponent(errorLabel);
}
if (!sender->GetSavesLoaded())

View File

@ -11,6 +11,7 @@ namespace ui
class Label;
class Spinner;
class Textbox;
class DropDown;
}
class SearchModel;
@ -32,6 +33,7 @@ private:
ui::Label * pageCountLabel;
ui::Label * tagsLabel;
ui::RichLabel * motdLabel = nullptr;
ui::DropDown * dateRange;
ui::Button * sortButton;
ui::Button * ownButton;
ui::Spinner * loadingSpinner;
@ -52,6 +54,7 @@ public:
void NotifySaveListChanged(SearchModel * sender);
void NotifySelectedChanged(SearchModel * sender);
void NotifyPageChanged(SearchModel * sender);
void NotifyPeriodChanged(SearchModel * sender);
void NotifySortChanged(SearchModel * sender);
void NotifyShowOwnChanged(SearchModel * sender);
void NotifyShowFavouriteChanged(SearchModel * sender);

View File

@ -30,6 +30,7 @@ TagsView::TagsView():
tagInput->Appearance.icon = IconTag;
tagInput->Appearance.HorizontalAlign = ui::Appearance::AlignLeft;
tagInput->Appearance.VerticalAlign = ui::Appearance::AlignMiddle;
tagInput->SetLimit(16);
AddComponent(tagInput);
FocusComponent(tagInput);

View File

@ -151,7 +151,7 @@ void UpdateActivity::NotifyError(Task * sender)
new ConfirmPrompt("Autoupdate failed", sb.Build(), { [this] {
if constexpr (!USE_UPDATESERVER)
{
Platform::OpenURI(ByteString(SCHEME) + "powdertoy.co.uk/Download.html");
Platform::OpenURI(ByteString::Build(SCHEME, SERVER, "/Download.html"));
}
Exit();
}, [this] { Exit(); } });

View File

@ -1,37 +1,26 @@
#include "CommandInterface.h"
#include <cstring>
#include <cstddef>
#include <cassert>
#include "Misc.h"
#include "gui/game/GameModel.h"
#include "simulation/Particle.h"
#include "Format.h"
#include "simulation/Simulation.h"
#include "simulation/Air.h"
#include "simulation/ElementClasses.h"
#include "gui/game/GameController.h"
#include "gui/game/GameModel.h"
#include "gui/interface/Engine.h"
#include "common/tpt-compat.h"
#include <cassert>
#include <cmath>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include <deque>
CommandInterface *commandInterface = nullptr;
CommandInterface::CommandInterface(GameController * c, GameModel * m)
CommandInterface::CommandInterface(GameController *newGameController, GameModel *newGameModel)
{
assert(!commandInterface);
commandInterface = this;
this->m = m;
this->c = c;
}
CommandInterface::~CommandInterface()
{
commandInterface = nullptr;
}
int CommandInterface::Command(String command)
{
lastError = "No interpreter";
return -1;
}
String CommandInterface::FormatCommand(String command)
{
return command;
this->m = newGameModel;
this->c = newGameController;
}
void CommandInterface::Log(LogType type, String message)
@ -81,3 +70,592 @@ String CommandInterface::GetLastError()
{
return lastError;
}
int CommandInterface::PlainCommand(String command)
{
lastError = "";
std::deque<String> words;
std::deque<AnyType> commandWords;
int retCode = -1;
//Split command into words, put them on the stack
for(String word : command.PartitionBy(' '))
words.push_back(word);
while(!words.empty())
{
try
{
commandWords.push_back(eval(&words));
}
catch (GeneralException & e)
{
retCode = -1;
lastError = e.GetExceptionMessage();
break;
}
}
if(commandWords.size())
{
retCode = 0;
lastError = ((StringType)commandWords.front()).Value();
}
//Evaluate
return retCode;
}
ValueType CommandInterface::testType(String word)
{
size_t i = 0;
String::value_type const *rawWord = word.c_str();
//Function
if (word == "set")
return TypeFunction;
else if (word == "create")
return TypeFunction;
else if (word == "delete")
return TypeFunction;
else if (word == "kill")
return TypeFunction;
else if (word == "load")
return TypeFunction;
else if (word == "reset")
return TypeFunction;
else if (word == "bubble")
return TypeFunction;
else if (word == "quit")
return TypeFunction;
//Basic type
for (i = 0; i < word.length(); i++)
{
if (!(rawWord[i] >= '0' && rawWord[i] <= '9') && !(rawWord[i] == '-' && !i))
{
if (rawWord[i] == '.' && rawWord[i+1])
goto parseFloat;
else if (rawWord[i] == ',' && rawWord[i+1] >= '0' && rawWord[i+1] <= '9')
goto parsePoint;
else if ((rawWord[i] == '#' || (i && rawWord[i-1] == '0' && rawWord[i] == 'x')) &&
((rawWord[i+1] >= '0' && rawWord[i+1] <= '9')
|| (rawWord[i+1] >= 'a' && rawWord[i+1] <= 'f')
|| (rawWord[i+1] >= 'A' && rawWord[i+1] <= 'F')))
goto parseNumberHex;
else
goto parseString;
}
}
return TypeNumber;
parseFloat:
for (i++; i < word.length(); i++)
if (!((rawWord[i] >= '0' && rawWord[i] <= '9')))
{
goto parseString;
}
return TypeFloat;
parseNumberHex:
for (i++; i < word.length(); i++)
if (!((rawWord[i] >= '0' && rawWord[i] <= '9') || (rawWord[i] >= 'a' && rawWord[i] <= 'f') || (rawWord[i] >= 'A' && rawWord[i] <= 'F')))
{
goto parseString;
}
return TypeNumber;
parsePoint:
for (i++; i < word.length(); i++)
if (!(rawWord[i] >= '0' && rawWord[i] <= '9'))
{
goto parseString;
}
return TypePoint;
parseString:
return TypeString;
}
int CommandInterface::parseNumber(String str)
{
String::value_type const *stringData = str.c_str();
char cc;
int base = 10;
int currentNumber = 0;
if (stringData[0] == '#')
{
stringData++;
base = 16;
}
else if (stringData[0] == '0' && stringData[1] == 'x')
{
stringData+=2;
base = 16;
}
if (base == 16)
{
while ((cc = *(stringData++)))
{
currentNumber *= base;
if (cc >= '0' && cc <= '9')
currentNumber += cc - '0';
else if (cc >= 'a' && cc <= 'f')
currentNumber += (cc - 'a') + 10;
else if (cc >= 'A' && cc <= 'F')
currentNumber += (cc - 'A') + 10;
else
break;
}
}
else
{
try
{
return str.ToNumber<int>();
}
catch (std::exception & e)
{
throw GeneralException(ByteString(e.what()).FromUtf8());
}
}
return currentNumber;
}
AnyType CommandInterface::eval(std::deque<String> * words)
{
if(words->size() < 1)
return AnyType(TypeNull, ValueValue());
String word = words->front(); words->pop_front();
ValueType wordType = testType(word);
switch(wordType)
{
case TypeFunction:
if(word == "set")
return tptS_set(words);
else if(word == "create")
return tptS_create(words);
else if(word == "delete" || word == "kill")
return tptS_delete(words);
else if(word == "load")
return tptS_load(words);
else if(word == "reset")
return tptS_reset(words);
else if(word == "bubble")
return tptS_bubble(words);
else if(word == "quit")
return tptS_quit(words);
break;
case TypeNumber:
return NumberType(parseNumber(word));
case TypeFloat:
return FloatType(atof(word.ToUtf8().c_str()));
case TypePoint:
{
int x, y;
if(String::Split comma = word.SplitNumber(x))
if(comma.After().BeginsWith(","))
if(comma.After().Substr(1).SplitNumber(y))
return PointType(x, y);
return PointType(0, 0);
}
case TypeString:
return StringType(word);
default:
break;
}
return StringType(word);
}
String CommandInterface::PlainFormatCommand(String command)
{
std::deque<String> words;
std::deque<AnyType> commandWords;
String outputData;
//Split command into words, put them on the stack
for(String word : command.PartitionBy(' ', true))
words.push_back(word);
while(!words.empty())
{
ValueType cType = testType(words.front());
switch(cType)
{
case TypeFunction:
outputData += "\bt";
break;
case TypeNumber:
case TypePoint:
outputData += "\bo";
break;
case TypeString:
outputData += "\bg";
break;
default:
outputData += "\bw";
break;
}
outputData += words.front() + " ";
words.pop_front();
}
return outputData;
}
AnyType CommandInterface::tptS_set(std::deque<String> * words)
{
auto &sd = SimulationData::CRef();
//Arguments from stack
StringType property = eval(words);
AnyType selector = eval(words);
AnyType value = eval(words);
Simulation * sim = m->GetSimulation();
unsigned char * partsBlock = (unsigned char*)&sim->parts[0];
int returnValue = 0;
FormatType propertyFormat;
int propertyOffset = GetPropertyOffset(property.Value().ToUtf8(), propertyFormat);
if (propertyOffset == -1)
throw GeneralException("Invalid property");
//Selector
int newValue = 0;
float newValuef = 0.0f;
if (property.Value() == "temp")
{
// convert non-string temperature values to strings to format::StringToTemperature can take care of them
switch (value.GetType())
{
case TypeNumber:
value = StringType(String::Build(((NumberType)value).Value()));
break;
case TypeFloat:
value = StringType(String::Build(((FloatType)value).Value()));
break;
default:
break;
}
}
if (value.GetType() == TypeNumber)
{
newValuef = float(newValue = ((NumberType)value).Value());
}
else if (value.GetType() == TypeFloat)
{
newValue = int(newValuef = ((FloatType)value).Value());
}
else if(value.GetType() == TypeString)
{
if (property.Value() == "temp")
{
try
{
newValuef = format::StringToTemperature(((StringType)value).Value(), c->GetTemperatureScale());
}
catch (const std::exception &ex)
{
throw GeneralException("Invalid value for assignment");
}
}
else
{
newValue = sd.GetParticleType(((StringType)value).Value().ToUtf8());
if (newValue < 0 || newValue >= PT_NUM)
{
// TODO: add element CAKE to invalidate this
if (((StringType)value).Value().ToUpper() == "CAKE")
throw GeneralException("Cake is a lie, not an element");
throw GeneralException("Invalid element");
}
}
}
else
throw GeneralException("Invalid value for assignment");
if (property.Value() == "type" && (newValue < 0 || newValue >= PT_NUM || !sd.elements[newValue].Enabled))
throw GeneralException("Invalid element");
if (selector.GetType() == TypePoint || selector.GetType() == TypeNumber)
{
int partIndex = -1;
if(selector.GetType() == TypePoint)
{
ui::Point tempPoint = ((PointType)selector).Value();
if(tempPoint.X<0 || tempPoint.Y<0 || tempPoint.Y >= YRES || tempPoint.X >= XRES)
throw GeneralException("Invalid position");
}
else
partIndex = ((NumberType)selector).Value();
if(partIndex<0 || partIndex>=NPART || sim->parts[partIndex].type==0)
throw GeneralException("Invalid particle");
switch(propertyFormat)
{
case FormatInt:
*((int*)(partsBlock+(partIndex*sizeof(Particle))+propertyOffset)) = newValue;
break;
case FormatFloat:
*((float*)(partsBlock+(partIndex*sizeof(Particle))+propertyOffset)) = newValuef;
break;
case FormatElement:
sim->part_change_type(partIndex, int(sim->parts[partIndex].x + 0.5f), int(sim->parts[partIndex].y + 0.5f), newValue);
break;
default:
break;
}
returnValue = 1;
}
else if (selector.GetType() == TypeString && ((StringType)selector).Value() == "all")
{
switch(propertyFormat)
{
case FormatInt:
{
for(int j = 0; j < NPART; j++)
if(sim->parts[j].type)
{
returnValue++;
*((int*)(partsBlock+(j*sizeof(Particle))+propertyOffset)) = newValue;
}
}
break;
case FormatFloat:
{
for(int j = 0; j < NPART; j++)
if(sim->parts[j].type)
{
returnValue++;
*((float*)(partsBlock+(j*sizeof(Particle))+propertyOffset)) = newValuef;
}
}
break;
case FormatElement:
{
for (int j = 0; j < NPART; j++)
if (sim->parts[j].type)
{
returnValue++;
sim->part_change_type(j, int(sim->parts[j].x + 0.5f), int(sim->parts[j].y + 0.5f), newValue);
}
}
break;
default:
break;
}
}
else if(selector.GetType() == TypeString || selector.GetType() == TypeNumber)
{
int type = 0;
if (selector.GetType() == TypeNumber)
type = ((NumberType)selector).Value();
else if (selector.GetType() == TypeString)
type = sd.GetParticleType(((StringType)selector).Value().ToUtf8());
if (type<0 || type>=PT_NUM)
throw GeneralException("Invalid particle type");
if (type==0)
throw GeneralException("Cannot set properties of particles that do not exist");
switch(propertyFormat)
{
case FormatInt:
{
for (int j = 0; j < NPART; j++)
if (sim->parts[j].type == type)
{
returnValue++;
*((int*)(partsBlock+(j*sizeof(Particle))+propertyOffset)) = newValue;
}
}
break;
case FormatFloat:
{
for (int j = 0; j < NPART; j++)
if (sim->parts[j].type == type)
{
returnValue++;
*((float*)(partsBlock+(j*sizeof(Particle))+propertyOffset)) = newValuef;
}
}
break;
case FormatElement:
{
for (int j = 0; j < NPART; j++)
if (sim->parts[j].type == type)
{
returnValue++;
sim->part_change_type(j, int(sim->parts[j].x + 0.5f), int(sim->parts[j].y + 0.5f), newValue);
}
}
break;
default:
break;
}
}
else
throw GeneralException("Invalid selector");
return NumberType(returnValue);
}
AnyType CommandInterface::tptS_create(std::deque<String> * words)
{
auto &sd = SimulationData::CRef();
//Arguments from stack
AnyType createType = eval(words);
PointType position = eval(words);
Simulation * sim = m->GetSimulation();
int type;
if(createType.GetType() == TypeNumber)
type = ((NumberType)createType).Value();
else if(createType.GetType() == TypeString)
type = sd.GetParticleType(((StringType)createType).Value().ToUtf8());
else
throw GeneralException("Invalid type");
if(type == -1)
throw GeneralException("Invalid particle type");
ui::Point tempPoint = position.Value();
if(tempPoint.X<0 || tempPoint.Y<0 || tempPoint.Y >= YRES || tempPoint.X >= XRES)
throw GeneralException("Invalid position");
int v = -1;
if (ID(type))
{
v = ID(type);
type = TYP(type);
}
int returnValue = sim->create_part(-1, tempPoint.X, tempPoint.Y, type, v);
return NumberType(returnValue);
}
AnyType CommandInterface::tptS_delete(std::deque<String> * words)
{
//Arguments from stack
AnyType partRef = eval(words);
Simulation * sim = m->GetSimulation();
if(partRef.GetType() == TypePoint)
{
ui::Point deletePoint = ((PointType)partRef).Value();
if(deletePoint.X<0 || deletePoint.Y<0 || deletePoint.Y >= YRES || deletePoint.X >= XRES)
throw GeneralException("Invalid position");
sim->delete_part(deletePoint.X, deletePoint.Y);
}
else if(partRef.GetType() == TypeNumber)
{
int partIndex = ((NumberType)partRef).Value();
if(partIndex < 0 || partIndex >= NPART)
throw GeneralException("Invalid particle index");
sim->kill_part(partIndex);
}
else
throw GeneralException("Invalid particle reference");
return NumberType(0);
}
AnyType CommandInterface::tptS_load(std::deque<String> * words)
{
//Arguments from stack
NumberType saveID = eval(words);
if (saveID.Value() > 0)
{
c->OpenSavePreview(saveID.Value(), 0, savePreviewNormal);
return NumberType(0);
}
else
throw GeneralException("Invalid save ID");
}
AnyType CommandInterface::tptS_bubble(std::deque<String> * words)
{
//Arguments from stack
PointType bubblePosA = eval(words);
ui::Point bubblePos = bubblePosA.Value();
if(bubblePos.X<0 || bubblePos.Y<0 || bubblePos.Y >= YRES || bubblePos.X >= XRES)
throw GeneralException("Invalid position");
Simulation * sim = m->GetSimulation();
int first, rem1, rem2;
first = sim->create_part(-1, bubblePos.X+18, bubblePos.Y, PT_SOAP);
rem1 = first;
for (int i = 1; i<=30; i++)
{
rem2 = sim->create_part(-1, int(bubblePos.X+18*cosf(i/5.0)+0.5f), int(bubblePos.Y+18*sinf(i/5.0)+0.5f), PT_SOAP);
sim->parts[rem1].ctype = 7;
sim->parts[rem1].tmp = rem2;
sim->parts[rem2].tmp2 = rem1;
rem1 = rem2;
}
sim->parts[rem1].ctype = 7;
sim->parts[rem1].tmp = first;
sim->parts[first].tmp2 = rem1;
sim->parts[first].ctype = 7;
return NumberType(0);
}
AnyType CommandInterface::tptS_reset(std::deque<String> * words)
{
auto &sd = SimulationData::CRef();
//Arguments from stack
StringType reset = eval(words);
String resetStr = reset.Value();
Simulation * sim = m->GetSimulation();
if (resetStr == "pressure")
{
for (int nx = 0; nx < XCELLS; nx++)
for (int ny = 0; ny < YCELLS; ny++)
{
sim->air->pv[ny][nx] = 0;
}
}
else if (resetStr == "velocity")
{
for (int nx = 0; nx < XCELLS; nx++)
for (int ny = 0; ny < YCELLS; ny++)
{
sim->air->vx[ny][nx] = 0;
sim->air->vy[ny][nx] = 0;
}
}
else if (resetStr == "sparks")
{
c->ResetSpark();
}
else if (resetStr == "temp")
{
for (int i = 0; i < NPART; i++)
{
if (sim->parts[i].type)
{
sim->parts[i].temp = sd.elements[sim->parts[i].type].DefaultProperties.temp;
}
}
}
else
{
throw GeneralException("Unknown reset command");
}
return NumberType(0);
}
AnyType CommandInterface::tptS_quit(std::deque<String> * words)
{
ui::Engine::Ref().Exit();
return NumberType(0);
}

View File

@ -1,41 +1,58 @@
#pragma once
#include "CommandInterfacePtr.h"
#include "common/ExplicitSingleton.h"
#include "common/String.h"
#include "gui/game/GameControllerEvents.h"
#include "TPTSTypes.h"
#include <deque>
class GameModel;
class GameController;
class Tool;
class CommandInterface
class CommandInterface : public ExplicitSingleton<CommandInterface>
{
protected:
String lastError;
GameModel * m;
GameController * c;
CommandInterface(GameController * c, GameModel * m);
int PlainCommand(String command);
String PlainFormatCommand(String command);
public:
CommandInterface(GameController *newGameController, GameModel *newGameModel);
enum LogType { LogError, LogWarning, LogNotice };
enum FormatType { FormatInt, FormatString, FormatChar, FormatFloat, FormatElement };
int GetPropertyOffset(ByteString key, FormatType & format);
void Log(LogType type, String message);
//void AttachGameModel(GameModel * m);
virtual void OnTick() { }
virtual void Init() { }
void OnTick();
void Init();
virtual bool HandleEvent(const GameControllerEvent &event) { return true; }
bool HandleEvent(const GameControllerEvent &event);
virtual int Command(String command);
virtual String FormatCommand(String command);
int Command(String command);
String FormatCommand(String command);
void SetLastError(String err)
{
lastError = err;
}
String GetLastError();
virtual ~CommandInterface();
static CommandInterface *Create(GameController * c, GameModel * m);
AnyType eval(std::deque<String> * words);
int parseNumber(String str);
AnyType tptS_set(std::deque<String> * words);
AnyType tptS_create(std::deque<String> * words);
AnyType tptS_delete(std::deque<String> * words);
AnyType tptS_load(std::deque<String> * words);
AnyType tptS_reset(std::deque<String> * words);
AnyType tptS_bubble(std::deque<String> * words);
AnyType tptS_quit(std::deque<String> * words);
ValueType testType(String word);
static CommandInterfacePtr Create(GameController *newGameController, GameModel *newGameModel);
};
extern CommandInterface *commandInterface;

View File

@ -0,0 +1,9 @@
#pragma once
#include <memory>
class CommandInterface;
struct CommandInterfaceDeleter
{
void operator ()(CommandInterface *ptr) const;
};
using CommandInterfacePtr = std::unique_ptr<CommandInterface, CommandInterfaceDeleter>;

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
#include "LuaScriptInterface.h"
#include "gui/interface/Button.h"
const char LuaButton::className[] = "Button";
const char LuaButton::className[] = "button";
#define method(class, name) {#name, &class::name}
Luna<LuaButton>::RegType LuaButton::methods[] = {
@ -15,53 +15,53 @@ Luna<LuaButton>::RegType LuaButton::methods[] = {
{0, 0}
};
LuaButton::LuaButton(lua_State * l) :
LuaComponent(l)
LuaButton::LuaButton(lua_State *L) :
LuaComponent(L)
{
int posX = luaL_optinteger(l, 1, 0);
int posY = luaL_optinteger(l, 2, 0);
int sizeX = luaL_optinteger(l, 3, 10);
int sizeY = luaL_optinteger(l, 4, 10);
String text = tpt_lua_optString(l, 5, "");
String toolTip = tpt_lua_optString(l, 6, "");
int posX = luaL_optinteger(L, 1, 0);
int posY = luaL_optinteger(L, 2, 0);
int sizeX = luaL_optinteger(L, 3, 10);
int sizeY = luaL_optinteger(L, 4, 10);
String text = tpt_lua_optString(L, 5, "");
String toolTip = tpt_lua_optString(L, 6, "");
button = new ui::Button(ui::Point(posX, posY), ui::Point(sizeX, sizeY), text, toolTip);
component = button;
button->SetActionCallback({ [this] { triggerAction(); } });
}
int LuaButton::enabled(lua_State * l)
int LuaButton::enabled(lua_State *L)
{
int args = lua_gettop(l);
int args = lua_gettop(L);
if(args)
{
luaL_checktype(l, 1, LUA_TBOOLEAN);
button->Enabled = lua_toboolean(l, 1);
luaL_checktype(L, 1, LUA_TBOOLEAN);
button->Enabled = lua_toboolean(L, 1);
return 0;
}
else
{
lua_pushboolean(l, button->Enabled);
lua_pushboolean(L, button->Enabled);
return 1;
}
}
int LuaButton::action(lua_State * l)
int LuaButton::action(lua_State *L)
{
return actionFunction.CheckAndAssignArg1(l);
return actionFunction.CheckAndAssignArg1(L);
}
int LuaButton::text(lua_State * l)
int LuaButton::text(lua_State *L)
{
int args = lua_gettop(l);
int args = lua_gettop(L);
if(args)
{
button->SetText(tpt_lua_checkString(l, 1));
button->SetText(tpt_lua_checkString(L, 1));
return 0;
}
else
{
tpt_lua_pushString(l, button->GetText());
tpt_lua_pushString(L, button->GetText());
return 1;
}
}
@ -70,11 +70,11 @@ void LuaButton::triggerAction()
{
if(actionFunction)
{
lua_rawgeti(l, LUA_REGISTRYINDEX, actionFunction);
lua_rawgeti(l, LUA_REGISTRYINDEX, owner_ref);
if (tpt_lua_pcall(l, 1, 0, 0, eventTraitNone))
lua_rawgeti(L, LUA_REGISTRYINDEX, actionFunction);
lua_rawgeti(L, LUA_REGISTRYINDEX, owner_ref);
if (tpt_lua_pcall(L, 1, 0, 0, eventTraitNone))
{
ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1));
ci->Log(CommandInterface::LogError, tpt_lua_toString(L, -1));
}
}
}

View File

@ -15,13 +15,13 @@ class LuaButton: public LuaComponent
ui::Button * button;
LuaComponentCallback actionFunction;
void triggerAction();
int action(lua_State * l);
int text(lua_State * l);
int enabled(lua_State * l);
int action(lua_State *L);
int text(lua_State *L);
int enabled(lua_State *L);
public:
static const char className[];
static Luna<LuaButton>::RegType methods[];
LuaButton(lua_State * l);
LuaButton(lua_State *L);
~LuaButton();
};

64
src/lua/LuaBz2.cpp Normal file
View File

@ -0,0 +1,64 @@
#include "LuaScriptInterface.h"
#include "bzip2/bz2wrap.h"
static int compress(lua_State *L)
{
auto src = tpt_lua_checkByteString(L, 1);
auto maxSize = size_t(luaL_optinteger(L, 2, 0));
std::vector<char> dest;
auto result = BZ2WCompress(dest, src.data(), src.size(), maxSize);
#define RETURN_ERR(str) lua_pushnil(L); lua_pushinteger(L, int(result)); lua_pushliteral(L, str); return 3
switch (result)
{
case BZ2WCompressOk: break;
case BZ2WCompressNomem: RETURN_ERR("out of memory");
case BZ2WCompressLimit: RETURN_ERR("size limit exceeded");
}
#undef RETURN_ERR
tpt_lua_pushByteString(L, ByteString(dest.begin(), dest.end()));
return 1;
}
static int decompress(lua_State *L)
{
auto src = tpt_lua_checkByteString(L, 1);
auto maxSize = size_t(luaL_optinteger(L, 2, 0));
std::vector<char> dest;
auto result = BZ2WDecompress(dest, src.data(), src.size(), maxSize);
#define RETURN_ERR(str) lua_pushnil(L); lua_pushinteger(L, int(result)); lua_pushliteral(L, str); return 3
switch (result)
{
case BZ2WDecompressOk: break;
case BZ2WDecompressNomem: RETURN_ERR("out of memory");
case BZ2WDecompressLimit: RETURN_ERR("size limit exceeded");
case BZ2WDecompressType:
case BZ2WDecompressBad:
case BZ2WDecompressEof: RETURN_ERR("corrupted stream");
}
#undef RETURN_ERR
tpt_lua_pushByteString(L, ByteString(dest.begin(), dest.end()));
return 1;
}
void LuaBz2::Open(lua_State *L)
{
static const luaL_Reg reg[] = {
#define LFUNC(v) { #v, v }
LFUNC(compress),
LFUNC(decompress),
#undef LFUNC
{ NULL, NULL }
};
lua_newtable(L);
luaL_register(L, NULL, reg);
#define LCONSTAS(k, v) lua_pushinteger(L, int(v)); lua_setfield(L, -2, k)
LCONSTAS("COMPRESS_NOMEM" , BZ2WCompressNomem );
LCONSTAS("COMPRESS_LIMIT" , BZ2WCompressLimit );
LCONSTAS("DECOMPRESS_NOMEM", BZ2WDecompressNomem);
LCONSTAS("DECOMPRESS_LIMIT", BZ2WDecompressLimit);
LCONSTAS("DECOMPRESS_TYPE" , BZ2WDecompressType );
LCONSTAS("DECOMPRESS_BAD" , BZ2WDecompressBad );
LCONSTAS("DECOMPRESS_EOF" , BZ2WDecompressEof );
#undef LCONSTAS
lua_setglobal(L, "bz2");
}

View File

@ -2,7 +2,7 @@
#include "LuaScriptInterface.h"
#include "gui/interface/Checkbox.h"
const char LuaCheckbox::className[] = "Checkbox";
const char LuaCheckbox::className[] = "checkbox";
#define method(class, name) {#name, &class::name}
Luna<LuaCheckbox>::RegType LuaCheckbox::methods[] = {
@ -15,51 +15,51 @@ Luna<LuaCheckbox>::RegType LuaCheckbox::methods[] = {
{0, 0}
};
LuaCheckbox::LuaCheckbox(lua_State * l) :
LuaComponent(l)
LuaCheckbox::LuaCheckbox(lua_State *L) :
LuaComponent(L)
{
int posX = luaL_optinteger(l, 1, 0);
int posY = luaL_optinteger(l, 2, 0);
int sizeX = luaL_optinteger(l, 3, 10);
int sizeY = luaL_optinteger(l, 4, 10);
String text = tpt_lua_optString(l, 5, "");
int posX = luaL_optinteger(L, 1, 0);
int posY = luaL_optinteger(L, 2, 0);
int sizeX = luaL_optinteger(L, 3, 10);
int sizeY = luaL_optinteger(L, 4, 10);
String text = tpt_lua_optString(L, 5, "");
checkbox = new ui::Checkbox(ui::Point(posX, posY), ui::Point(sizeX, sizeY), text, "");
component = checkbox;
checkbox->SetActionCallback({ [this] { triggerAction(); } });
}
int LuaCheckbox::checked(lua_State * l)
int LuaCheckbox::checked(lua_State *L)
{
int args = lua_gettop(l);
int args = lua_gettop(L);
if(args)
{
checkbox->SetChecked(lua_toboolean(l, 1));
checkbox->SetChecked(lua_toboolean(L, 1));
return 0;
}
else
{
lua_pushboolean(l, checkbox->GetChecked());
lua_pushboolean(L, checkbox->GetChecked());
return 1;
}
}
int LuaCheckbox::action(lua_State * l)
int LuaCheckbox::action(lua_State *L)
{
return actionFunction.CheckAndAssignArg1(l);
return actionFunction.CheckAndAssignArg1(L);
}
int LuaCheckbox::text(lua_State * l)
int LuaCheckbox::text(lua_State *L)
{
int args = lua_gettop(l);
int args = lua_gettop(L);
if(args)
{
checkbox->SetText(tpt_lua_checkString(l, 1));
checkbox->SetText(tpt_lua_checkString(L, 1));
return 0;
}
else
{
tpt_lua_pushString(l, checkbox->GetText());
tpt_lua_pushString(L, checkbox->GetText());
return 1;
}
}
@ -68,12 +68,12 @@ void LuaCheckbox::triggerAction()
{
if(actionFunction)
{
lua_rawgeti(l, LUA_REGISTRYINDEX, actionFunction);
lua_rawgeti(l, LUA_REGISTRYINDEX, owner_ref);
lua_pushboolean(l, checkbox->GetChecked());
if (tpt_lua_pcall(l, 2, 0, 0, eventTraitNone))
lua_rawgeti(L, LUA_REGISTRYINDEX, actionFunction);
lua_rawgeti(L, LUA_REGISTRYINDEX, owner_ref);
lua_pushboolean(L, checkbox->GetChecked());
if (tpt_lua_pcall(L, 2, 0, 0, eventTraitNone))
{
ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1));
ci->Log(CommandInterface::LogError, tpt_lua_toString(L, -1));
}
}
}

View File

@ -15,13 +15,13 @@ class LuaCheckbox: public LuaComponent
ui::Checkbox * checkbox;
LuaComponentCallback actionFunction;
void triggerAction();
int action(lua_State * l);
int checked(lua_State * l);
int text(lua_State * l);
int action(lua_State *L);
int checked(lua_State *L);
int text(lua_State *L);
public:
static const char className[];
static Luna<LuaCheckbox>::RegType methods[];
LuaCheckbox(lua_State * l);
LuaCheckbox(lua_State *L);
~LuaCheckbox();
};

View File

@ -2,15 +2,13 @@
#if LUA_VERSION_NUM >= 502
// Implement missing luaL_typerror function
int luaL_typerror (lua_State *L, int narg, const char *tname)
int luaL_typerror(lua_State *L, int narg, const char *tname)
{
const char *msg = lua_pushfstring(L, "%s expected, got %s", tname, luaL_typename(L, narg));
return luaL_argerror(L, narg, msg);
}
void luaL_register (lua_State *L,
const char *libname,
const luaL_Reg *l)
void luaL_register(lua_State *L, const char *libname, const luaL_Reg *l)
{
if (libname)
{
@ -20,18 +18,7 @@ void luaL_register (lua_State *L,
}
luaL_setfuncs(L, l, 0);
}
void tpt_lua_setmainthread(lua_State *L)
{
}
void tpt_lua_getmainthread(lua_State *L)
{
lua_rawgeti(L, LUA_REGISTRYINDEX, LUA_RIDX_MAINTHREAD);
}
#else
# ifndef lua_pushglobaltable // * Thank you moonjit
// Implement function added in lua 5.2 that we now use
void lua_pushglobaltable(lua_State *L)
@ -39,42 +26,4 @@ void lua_pushglobaltable(lua_State *L)
lua_pushvalue(L, LUA_GLOBALSINDEX);
}
# endif
void tpt_lua_setmainthread(lua_State *L)
{
lua_pushthread(L);
lua_setfield(L, LUA_REGISTRYINDEX, "tpt_lua_mainthread");
}
void tpt_lua_getmainthread(lua_State *L)
{
lua_getfield(L, LUA_REGISTRYINDEX, "tpt_lua_mainthread");
}
#endif
// Useful helper function, mainly used for logging
int luaL_tostring(lua_State *L, int n)
{
luaL_checkany(L, n);
switch (lua_type(L, n))
{
case LUA_TNUMBER:
lua_tostring(L, n);
lua_pushvalue(L, n);
break;
case LUA_TSTRING:
lua_pushvalue(L, n);
break;
case LUA_TBOOLEAN:
lua_pushstring(L, (lua_toboolean(L, n) ? "true" : "false"));
break;
case LUA_TNIL:
lua_pushliteral(L, "nil");
break;
default:
lua_pushfstring(L, "%s: %p", luaL_typename(L, n), lua_topointer(L, n));
break;
}
return 1;
}

View File

@ -9,9 +9,6 @@ extern "C"
#include <lauxlib.h>
#include <lualib.h>
LUALIB_API void tpt_lua_setmainthread(lua_State *L);
LUALIB_API void tpt_lua_getmainthread(lua_State *L);
#if LUA_VERSION_NUM >= 502
void luaL_register(lua_State *L, const char *libname, const luaL_Reg *l);
#define lua_strlen(L,i) lua_rawlen(L, (i))
@ -25,7 +22,6 @@ LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname);
LUALIB_API void (lua_pushglobaltable) (lua_State *L);
# endif
#endif
int luaL_tostring(lua_State *L, int n);
#ifdef __cplusplus
}

Some files were not shown because too many files have changed in this diff Show More