Compare commits
80 Commits
snapshot-3
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
6ce8e10adb | ||
|
47384c5572 | ||
|
de345a85a1 | ||
|
6de252eb34 | ||
|
b39f3c7f55 | ||
|
58c0ab4747 | ||
|
73daf67c34 | ||
|
3edb8c4233 | ||
|
351dc6ec87 | ||
|
0cfb91ce86 | ||
|
efeac4fd8a | ||
|
36619df4f4 | ||
|
9f02999947 | ||
|
2c55a8a9d9 | ||
|
1a0eb73ea0 | ||
|
f8873debc6 | ||
|
51f714de0f | ||
|
e371d6345b | ||
|
e55fc8703a | ||
|
40e2e4a62a | ||
|
e9fdb254af | ||
|
5e8a28b946 | ||
|
a53595ce68 | ||
|
e8c24e7e23 | ||
|
7edc413cdc | ||
|
53b9b0e286 | ||
|
99cd354a16 | ||
|
d56e8387cf | ||
|
c1c1daa9e5 | ||
|
bfdfebc1d5 | ||
|
502df57cae | ||
|
a0ba5f5398 | ||
|
178519dbb0 | ||
|
bb471e63e1 | ||
|
4b866c409a | ||
|
3fb356e2a9 | ||
|
8452d96bf6 | ||
|
eef8943a3c | ||
|
5160f4ad33 | ||
|
7870ec56a3 | ||
|
16f50b808d | ||
|
aa8ee76fbb | ||
|
228d559ccd | ||
|
758d34c4eb | ||
|
596e6cece1 | ||
|
3a3a8c1cc3 | ||
|
f47d0a9828 | ||
|
7cd684ccfe | ||
|
82bcb0ef9e | ||
|
c85ebe4a0a | ||
|
ab28f93753 | ||
|
a7e71db9a0 | ||
|
d9172faa17 | ||
|
ef308c1e48 | ||
|
79f45eb096 | ||
|
0310ac08a8 | ||
|
ea7cd41975 | ||
|
f4b836deb1 | ||
|
a250894a12 | ||
|
c3cd4f1691 | ||
|
e6e36a6b7c | ||
|
588fe293ec | ||
|
69e0a8b0aa | ||
|
ae07c55f4d | ||
|
09ae62ac9e | ||
|
622df75c29 | ||
|
cd1855fbdd | ||
|
7a10847780 | ||
|
d87130bd66 | ||
|
1cb8f0378f | ||
|
2a43e8aef0 | ||
|
3f503bcb1c | ||
|
a637a619c9 | ||
|
5839657806 | ||
|
1ef0c1a3e0 | ||
|
452b553350 | ||
|
8e6faddd2f | ||
|
5e60b53a3b | ||
|
5eb5383f08 | ||
|
708d543d29 |
36
.github/build.sh
vendored
36
.github/build.sh
vendored
@ -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
|
||||
|
10
.github/force-msys2-bash.sh
vendored
10
.github/force-msys2-bash.sh
vendored
@ -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
9
.github/prepare.py
vendored
@ -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({
|
||||
|
4
.github/starcatcher-publish.sh
vendored
4
.github/starcatcher-publish.sh
vendored
@ -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
3
.github/vs-env.sh
vendored
@ -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
|
||||
|
62
.github/workflows/build.yaml
vendored
62
.github/workflows/build.yaml
vendored
@ -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 }}
|
||||
|
@ -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
|
||||
---------------------------------------------------------------------------
|
||||
|
@ -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"
|
||||
|
16
meson.build
16
meson.build
@ -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 + [
|
||||
|
@ -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'
|
||||
)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -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@
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
@ -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);
|
@ -14,4 +14,13 @@ namespace http
|
||||
sortByVotes,
|
||||
sortByDate,
|
||||
};
|
||||
|
||||
enum Period
|
||||
{
|
||||
allSaves,
|
||||
todaySaves,
|
||||
weekSaves,
|
||||
monthSaves,
|
||||
yearSaves,
|
||||
};
|
||||
}
|
||||
|
@ -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(¤tTime);
|
||||
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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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,
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
client_files = files(
|
||||
'MD5.cpp',
|
||||
'SaveFile.cpp',
|
||||
'SaveInfo.cpp',
|
||||
'ThumbnailRendererTask.cpp',
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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()
|
||||
|
@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include "common/String.h"
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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++)
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -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));
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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 = ®ularToolset[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 != ®ularToolset[0])
|
||||
{
|
||||
activeTools = regularToolset;
|
||||
activeTools = ®ularToolset[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{});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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; }
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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 ®ion : 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 ®ion : regions)
|
||||
{
|
||||
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(®ion.action))
|
||||
if (region.begin <= cursorPosition && region.end > cursorPosition)
|
||||
{
|
||||
Platform::OpenURI(linkAction->uri);
|
||||
return;
|
||||
if (auto *linkAction = std::get_if<RichTextRegion::LinkAction>(®ion.action))
|
||||
{
|
||||
Platform::OpenURI(linkAction->uri);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Label::OnMouseClick(x, y, button);
|
||||
Label::OnMouseDown(x, y, button);
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
@ -59,6 +59,8 @@ void LocalBrowserModel::OpenSave(int index)
|
||||
{
|
||||
stamp = std::move(savesList[index]);
|
||||
savesList.clear();
|
||||
notifyPageChanged();
|
||||
notifySavesListChanged();
|
||||
}
|
||||
|
||||
bool LocalBrowserModel::GetMoveToFront()
|
||||
|
@ -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;
|
||||
|
@ -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, ¤tY, &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);
|
||||
});
|
||||
|
@ -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)
|
||||
|
@ -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(); } });
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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)));
|
||||
|
@ -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(); }
|
||||
|
@ -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())
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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(); } });
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
9
src/lua/CommandInterfacePtr.h
Normal file
9
src/lua/CommandInterfacePtr.h
Normal 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
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
64
src/lua/LuaBz2.cpp
Normal 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");
|
||||
}
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
Reference in New Issue
Block a user