diff --git a/.github/build.sh b/.github/build.sh index a57d9802d..d3777e24a 100755 --- a/.github/build.sh +++ b/.github/build.sh @@ -43,6 +43,28 @@ aarch64-android-bionic-static) ;; *) >&2 echo "configuration $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC is not supported" && exit 1;; esac +if [[ -z ${BSH_NO_PACKAGES-} ]]; then + case $BSH_BUILD_PLATFORM in + linux) + sudo apt update + if [[ $BSH_STATIC_DYNAMIC == static ]]; then + sudo apt install libc6-dev libc6-dev-i386 + else + sudo apt install libluajit-5.1-dev libcurl4-openssl-dev libfftw3-dev zlib1g-dev libsdl2-dev libbz2-dev libjsoncpp-dev + fi + if [[ $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == windows-mingw ]]; then + sudo apt install g++-mingw-w64-x86-64 + fi + ;; + darwin) + brew install pkg-config binutils + if [[ $BSH_STATIC_DYNAMIC != static ]]; then + brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp + fi + ;; + esac +fi + function inplace_sed() { local subst=$1 local path=$2 @@ -163,7 +185,6 @@ meson_configure+=$'\t'-Dapp_data=$APP_DATA meson_configure+=$'\t'-Dapp_vendor=$APP_VENDOR meson_configure+=$'\t'-Db_strip=false meson_configure+=$'\t'-Db_staticpic=false -meson_configure+=$'\t'-Dinstall_check=true meson_configure+=$'\t'-Dmod_id=$MOD_ID case $BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_DEBUG_RELEASE in x86_64-linux-gnu-debug) ;& @@ -174,6 +195,12 @@ x86_64-darwin-macos-debug) meson_configure+=$'\t'-Dbuild_font=true ;; esac +if [[ $PACKAGE_MODE == nohttp ]]; then + meson_configure+=$'\t'-Dhttp=false +fi +if [[ $PACKAGE_MODE == nolua ]]; then + meson_configure+=$'\t'-Dlua=none +fi if [[ $BSH_STATIC_DYNAMIC == static ]]; then meson_configure+=$'\t'-Dstatic=prebuilt if [[ $BSH_HOST_PLATFORM == windows ]]; then @@ -212,9 +239,15 @@ fi if [[ $RELEASE_TYPE == stable ]]; then stable_or_beta=yes fi -save_version=$(grep -w src/Config.template.h -e "#define SAVE_VERSION" | cut -d ' ' -f 3) -minor_version=$(grep -w src/Config.template.h -e "#define MINOR_VERSION" | cut -d ' ' -f 3) -build_num=$(grep -w src/Config.template.h -e "#define BUILD_NUM" | cut -d ' ' -f 3) +set +e +save_version=$(cat src/Config.template.h | sed -n 's/constexpr int SAVE_VERSION * = \([^;]*\);/\1/p') +minor_version=$(cat src/Config.template.h | sed -n 's/constexpr int MINOR_VERSION * = \([^;]*\);/\1/p') +build_num=$(cat src/Config.template.h | sed -n 's/constexpr int BUILD_NUM * = \([^;]*\);/\1/p') +if [[ -z ${save_version-} ]] || [[ -z ${minor_version-} ]] || [[ -z ${build_num-} ]]; then + >&2 echo "failed to extract version from Config.template.h" + exit 1 +fi +set -e if [[ $stable_or_beta == yes ]] && [[ $MOD_ID != 0 ]]; then save_version=$(echo $RELEASE_NAME | cut -d '.' -f 1) minor_version=$(echo $RELEASE_NAME | cut -d '.' -f 2) @@ -252,9 +285,14 @@ if [[ $BSH_HOST_PLATFORM-$BSH_HOST_ARCH == darwin-aarch64 ]]; then meson_configure+=$'\t'--cross-file=.github/macaa64-ghactions.ini fi if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] || [[ $BSH_STATIC_DYNAMIC == static ]]); then - if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then - >&2 echo "GITHUB_REPOSITORY_OWNER not set" - exit 1 + if [[ -z ${TPTLIBSREMOTE-} ]]; then + if [[ -z "${GITHUB_REPOSITORY_OWNER-}" ]]; then + >&2 echo "GITHUB_REPOSITORY_OWNER not set" + exit 1 + fi + tptlibsremote=https://github.com/$GITHUB_REPOSITORY_OWNER/tpt-libs + else + tptlibsremote=$TPTLIBSREMOTE fi if [[ "$BSH_HOST_ARCH-$BSH_HOST_PLATFORM-$BSH_HOST_LIBC-$BSH_STATIC_DYNAMIC $BSH_BUILD_PLATFORM" == "x86_64-windows-mingw-dynamic linux" ]]; then >&2 echo "this configuration is not supported in tptlibsdev mode" @@ -262,10 +300,13 @@ if [[ $RELEASE_TYPE == tptlibsdev ]] && ([[ $BSH_HOST_PLATFORM == windows ]] || exit 0 fi tptlibsbranch=$(echo $RELEASE_NAME | cut -d '-' -f 2-) # $RELEASE_NAME is tptlibsdev-BRANCH + if [[ -d build-tpt-libs ]] && [[ ${TPTLIBSRESET-} == yes ]]; then + rm -rf build-tpt-libs + fi if [[ ! -d build-tpt-libs/tpt-libs ]]; then mkdir -p build-tpt-libs cd build-tpt-libs - git clone https://github.com/$GITHUB_REPOSITORY_OWNER/tpt-libs --branch $tptlibsbranch --depth 1 + git clone $tptlibsremote --branch $tptlibsbranch --depth 1 cd .. fi tpt_libs_vtag=v00000000000000 @@ -331,7 +372,7 @@ if [[ $BSH_HOST_PLATFORM == android ]]; then fi if [[ $PACKAGE_MODE == appimage ]]; then # so far this can only happen with $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == linux-gnu, but this may change later - meson configure -Dinstall_check=false -Dignore_updates=true -Dbuild_render=false -Dbuild_font=false + meson configure -Dcan_install=no -Dignore_updates=true -Dbuild_render=false -Dbuild_font=false strip_target=$APP_EXE fi if [[ $BSH_BUILD_PLATFORM == windows ]]; then @@ -341,6 +382,14 @@ if [[ $BSH_BUILD_PLATFORM == windows ]]; then set -e cat $APP_EXE.exe.rsp [[ $ninja_code == 0 ]]; + echo # rsps don't usually have a newline at the end + if [[ "$BSH_HOST_PLATFORM-$BSH_STATIC_DYNAMIC $BSH_BUILD_PLATFORM" == "windows-dynamic windows" ]]; then + # on windows we provide the dynamic dependencies also; makes sense to check for their presence + # msys ldd works fine but only on windows build machines + if ldd $APP_EXE | grep "not found"; then + exit 1 # ldd | grep will have printed missing deps + fi + fi else ninja -v fi @@ -383,8 +432,7 @@ if [[ $PACKAGE_MODE == dmg ]]; then mv $appdir dmgroot/$appdir cp ../LICENSE dmgroot/LICENSE cp ../README.md dmgroot/README.md - hdiutil create uncompressed.dmg -ov -volname $APP_NAME -fs HFS+ -srcfolder dmgroot - hdiutil convert uncompressed.dmg -format UDZO -o $ASSET_PATH + hdiutil create -format UDZO -volname $APP_NAME -fs HFS+ -srcfolder dmgroot -o $ASSET_PATH elif [[ $PACKAGE_MODE == appimage ]]; then # so far this can only happen with $BSH_HOST_PLATFORM-$BSH_HOST_LIBC == linux-gnu, but this may change later case $BSH_HOST_ARCH in diff --git a/.github/prepare.py b/.github/prepare.py index 6da65fd0c..a02ed2f99 100755 --- a/.github/prepare.py +++ b/.github/prepare.py @@ -78,11 +78,13 @@ build_matrix = [] publish_matrix = [] # consider disabling line wrapping to edit this monstrosity for arch, platform, libc, statdyn, bplatform, runson, suffix, publish, artifact, dbgsuffix, mode, starcatcher, dbgrel in [ - ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'debug' ), - ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', True, True, '.dbg', None, 'x86_64-lin-gcc-static', 'release' ), - ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-18.04', '', False, True, '.dbg', 'appimage', None, 'release' ), - ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'debug' ), - ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-18.04', '', False, False, None, None, None, 'release' ), + ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ), + ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', True, True, '.dbg', None, 'x86_64-lin-gcc-static', 'release' ), + ( 'x86_64', 'linux', 'gnu', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', 'appimage', None, 'release' ), + ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ), + ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nohttp', None, 'debug' ), + ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, 'nolua', None, 'debug' ), + ( 'x86_64', 'linux', 'gnu', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'release' ), # ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them # ( 'x86_64', 'windows', 'mingw', 'static', 'linux', 'ubuntu-20.04', '', False, True, '.dbg', None, None, 'release' ), # ubuntu-20.04 doesn't have windows TLS headers somehow and I haven't yet figured out how to get them ( 'x86_64', 'windows', 'mingw', 'dynamic', 'linux', 'ubuntu-20.04', '', False, False, None, None, None, 'debug' ), @@ -107,14 +109,14 @@ for arch, platform, libc, statdyn, bplatform, runson, suff ( 'aarch64', 'darwin', 'macos', 'static', 'darwin', 'macos-11.0', '.dmg', True, True, None, 'dmg', 'arm64-mac-gcc-static', 'release' ), # ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'debug' ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64 # ( 'aarch64', 'darwin', 'macos', 'dynamic', 'darwin', 'macos-11.0', '.dmg', False, False, None, 'dmg', None, 'release' ), # macos-11.0 is x86_64 and I haven't yet figured out how to get homebrew to install aarch64 libs on x86_64 - ( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ), - ( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release' ), - ( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ), - ( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'x86_64-and-gcc-static', 'release' ), - ( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ), - ( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'arm-and-gcc-static', 'release' ), - ( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', False, False, None, None, None, 'debug' ), - ( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-18.04', '.apk', True, True, '.dbg', None, 'arm64-and-gcc-static', 'release' ), + ( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ), + ( 'x86', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'i686-and-gcc-static', 'release' ), + ( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ), + ( 'x86_64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'x86_64-and-gcc-static', 'release' ), + ( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ), + ( 'arm', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm-and-gcc-static', 'release' ), + ( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', False, False, None, None, None, 'debug' ), + ( 'aarch64', 'android', 'bionic', 'static', 'linux', 'ubuntu-20.04', '.apk', True, True, '.dbg', None, 'arm64-and-gcc-static', 'release' ), ]: if not mode: mode = 'default' diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 450247501..a5533bbeb 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -31,6 +31,7 @@ jobs: app_vendor: ${{ steps.prepare.outputs.app_vendor }} do_publish: ${{ steps.prepare.outputs.do_publish }} steps: + - run: git config --global core.autocrlf false - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: @@ -58,21 +59,14 @@ jobs: fail-fast: false matrix: ${{ fromJSON(needs.prepare.outputs.build_matrix) }} steps: + - run: git config --global core.autocrlf false - uses: actions/checkout@v3 - uses: actions/setup-python@v4 with: python-version: '3.10' - run: python -m pip install meson ninja - if: matrix.bsh_build_platform == 'darwin' - run: brew install pkg-config coreutils binutils bash - - if: matrix.bsh_build_platform == 'darwin' && matrix.bsh_static_dynamic != 'static' - run: brew install luajit curl fftw zlib sdl2 bzip2 jsoncpp - - if: matrix.bsh_build_platform == 'linux' && matrix.bsh_host_libc == 'mingw' - run: sudo apt update && sudo apt install g++-mingw-w64-x86-64 - - if: matrix.bsh_build_platform == 'linux' && matrix.bsh_static_dynamic != 'static' - run: sudo apt update && sudo apt install libluajit-5.1-dev libcurl4-openssl-dev libfftw3-dev zlib1g-dev libsdl2-dev libbz2-dev libjsoncpp-dev - - if: matrix.bsh_build_platform == 'linux' && matrix.bsh_static_dynamic == 'static' - run: sudo apt update && sudo apt install libc6-dev libc6-dev-i386 + run: brew install bash coreutils - run: bash -c './.github/build.sh' env: BSH_HOST_ARCH: ${{ matrix.bsh_host_arch }} @@ -130,6 +124,7 @@ jobs: matrix: ${{ fromJSON(needs.prepare.outputs.publish_matrix) }} if: needs.prepare.outputs.do_publish == 'yes' steps: + - run: git config --global core.autocrlf false - uses: actions/checkout@v3 - uses: actions/download-artifact@v3 with: @@ -147,6 +142,7 @@ jobs: needs: [build, publish, prepare] if: needs.prepare.outputs.do_publish == 'yes' steps: + - run: git config --global core.autocrlf false - uses: actions/checkout@v3 - run: ./.github/starcatcher-release.sh env: diff --git a/README.md b/README.md index 7efc608fa..af48ad012 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -The Powder Toy - December 2022 +The Powder Toy - January 2023 ========================== Get the latest version [from the Powder Toy website](https://powdertoy.co.uk/Download.html). @@ -25,7 +25,7 @@ Thanks * Skresanov Savely * cracker64 * Catelite -* Bryan Hoyle +* Victoria Hoyle * Nathan Cousins * jacksonmj * Felix Wallin @@ -111,15 +111,16 @@ Controls Command Line --------------------------------------------------------------------------- -| Command | Description | Example | -| --------------------- | ------------------------------------------------ | --------------------------------- | -| `scale:SIZE` | Change window scale factor | `scale:2` | -| `kiosk` | Fullscreen mode | | -| `proxy:SERVER[:PORT]` | Proxy server to use | `proxy:wwwcache.lancs.ac.uk:8080` | -| `open FILE` | Opens the file as a stamp or game save | | -| `ddir DIRECTORY` | Directory used for saving stamps and preferences | | -| `ptsave:SAVEID` | Open online save, used by ptsave: URLs | `ptsave:2198` | -| `disable-network` | Disables internet connections | | -| `redirect` | Redirects output to stdout.txt / stderr.txt | | +| Command | Description | Example | +| --------------------- | ------------------------------------------------ | --------------------------------------------| +| `scale:SIZE` | Change window scale factor | `scale:2` | +| `kiosk` | Fullscreen mode | | +| `proxy:SERVER[:PORT]` | Proxy server to use | `proxy:wwwcache.lancs.ac.uk:8080` | +| `open FILE` | Opens the file as a stamp or game save | | +| `ddir DIRECTORY` | Directory used for saving stamps and preferences | | +| `ptsave:SAVEID` | Open online save, used by ptsave: URLs | `ptsave:2198` | +| `disable-network` | Disables internet connections | | +| `disable-bluescreen` | Disable bluescreen handler | | +| `redirect` | Redirects output to stdout.txt / stderr.txt | | | `cafile:CAFILE` | Set certificate bundle path | `cafile:/etc/ssl/certs/ca-certificates.crt` | -| `capath:CAPATH` | Set certificate directory path | `capath:/etc/ssl/certs` | +| `capath:CAPATH` | Set certificate directory path | `capath:/etc/ssl/certs` | diff --git a/meson.build b/meson.build index 513351ee2..ee9f0c862 100644 --- a/meson.build +++ b/meson.build @@ -37,6 +37,9 @@ host_arch = host_machine.cpu_family() host_platform = host_machine.system() # educated guesses follow, PRs welcome if c_compiler.get_id() in [ 'msvc' ] + if host_platform != 'windows' + error('this seems fishy') + endif host_libc = 'msvc' elif c_compiler.get_id() in [ 'gcc' ] and host_platform == 'windows' host_libc = 'mingw' @@ -49,6 +52,11 @@ elif host_platform in [ 'android' ] host_platform = 'android' host_libc = 'bionic' else + if host_platform != 'linux' + # TODO: maybe use 'default' in place of 'linux', or use something other than host_platform where details such as desktop integration are concerned + warning('host platform is not linux but we will pretend that it is') + host_platform = 'linux' + endif host_libc = 'gnu' endif @@ -64,11 +72,7 @@ endif is_static = static_variant != 'none' is_debug = get_option('optimization') in [ '0', 'g' ] -enforce_https = get_option('enforce_https') - -if not is_debug and not enforce_https - error('refusing to build a release binary with enforce_https=false') -endif +app_exe = get_option('app_exe') tpt_libs_static = 'none' if static_variant == 'prebuilt' @@ -81,7 +85,7 @@ tpt_libs_debug = is_debug ? 'debug' : 'release' tpt_libs_variant = '@0@-@1@-@2@-@3@'.format(host_arch, host_platform, host_libc, tpt_libs_static) tpt_libs_vtag = get_option('tpt_libs_vtag') if tpt_libs_vtag == '' - tpt_libs_vtag = 'v20221223184203' + tpt_libs_vtag = 'v20230205154205' endif if tpt_libs_static != 'none' if tpt_libs_variant not in [ @@ -122,10 +126,6 @@ else endif endif -conf_data = configuration_data() -conf_data.set('CURL_STATICLIB', false) -conf_data.set('ZLIB_WINAPI', false) - x86_sse_level_str = get_option('x86_sse') if x86_sse_level_str == 'auto' x86_sse_level = 20 @@ -179,9 +179,7 @@ if host_platform == 'android' endif curl_dep = enable_http ? dependency('libcurl', static: is_static) : [] -enable_gravfft = get_option('gravfft') -fftw_dep = enable_gravfft ? dependency('fftw3f', static: is_static) : [] - +fftw_dep = dependency('fftw3f', static: is_static) threads_dep = dependency('threads') zlib_dep = dependency('zlib', static: is_static) png_dep = dependency('libpng16', static: is_static) @@ -278,13 +276,11 @@ endif if host_platform == 'windows' args_ccomp_win = [ '-D_WIN32_WINNT=0x0501' ] - project_c_args += args_ccomp_win - project_cpp_args += args_ccomp_win windows_mod = import('windows') if is_static - conf_data.set('CURL_STATICLIB', true) + args_ccomp_win += [ '-DCURL_STATICLIB' ] if host_arch == 'x86_64' - conf_data.set('ZLIB_WINAPI', true) + args_ccomp_win += [ '-DZLIB_WINAPI' ] endif else foreach input_output_condition : tpt_libs.get_variable('config_dlls') @@ -302,6 +298,8 @@ if host_platform == 'windows' endif endforeach endif + project_c_args += args_ccomp_win + project_cpp_args += args_ccomp_win endif project_inc = include_directories([ 'src', 'resources' ]) @@ -316,43 +314,6 @@ else ident_platform = 'UNKNOWN' endif -app_exe = get_option('app_exe') -app_id = get_option('app_id') - -conf_data.set('LIN', host_platform == 'linux') -conf_data.set('AND', host_platform == 'android') -conf_data.set('WIN', host_platform == 'windows') -conf_data.set('MACOSX', host_platform == 'darwin') -conf_data.set('X86', is_x86) -conf_data.set('X86_SSE3', x86_sse_level >= 30) -conf_data.set('X86_SSE2', x86_sse_level >= 20) -conf_data.set('X86_SSE', x86_sse_level >= 10) -conf_data.set('_64BIT', is_64bit) -conf_data.set('BETA', get_option('beta')) -conf_data.set('NO_INSTALL_CHECK', not get_option('install_check')) -conf_data.set('IGNORE_UPDATES', get_option('ignore_updates')) -conf_data.set('MOD_ID', get_option('mod_id')) -conf_data.set('DEBUG', is_debug) -conf_data.set('SNAPSHOT', get_option('snapshot')) -conf_data.set('SNAPSHOT_ID', get_option('snapshot_id')) -conf_data.set('SERVER', '"@0@"'.format(get_option('server'))) -conf_data.set('STATICSERVER', '"@0@"'.format(get_option('static_server'))) -conf_data.set('IDENT_PLATFORM', '"@0@"'.format(ident_platform)) -conf_data.set('IDENT', '"@0@-@1@-@2@"'.format(host_arch, host_platform, host_libc).to_upper()) -conf_data.set('ENFORCE_HTTPS', enforce_https) -conf_data.set('APPNAME', get_option('app_name')) -conf_data.set('APPCOMMENT', get_option('app_comment')) -conf_data.set('APPEXE', app_exe) -conf_data.set('APPID', app_id) -conf_data.set('APPDATA', get_option('app_data')) -conf_data.set('APPVENDOR', get_option('app_vendor')) - -if get_option('update_server') != '' - conf_data.set('UPDATESERVER', '"@0@"'.format(get_option('update_server'))) -else - conf_data.set('UPDATESERVER', false) -endif - data_files = [] subdir('src') @@ -378,7 +339,7 @@ if get_option('build_powder') powder_sha = shared_library( app_exe, sources: powder_files, - include_directories: [ project_inc, powder_inc ], + include_directories: project_inc, c_args: project_c_args, cpp_args: project_cpp_args, link_args: project_link_args, @@ -389,12 +350,13 @@ if get_option('build_powder') executable( app_exe, sources: powder_files, - include_directories: [ project_inc, powder_inc ], + include_directories: project_inc, c_args: project_c_args, cpp_args: project_cpp_args, win_subsystem: is_debug ? 'console' : 'windows', link_args: project_link_args, dependencies: powder_deps, + install: true, ) endif endif @@ -414,7 +376,7 @@ if get_option('build_render') executable( 'render', sources: render_files, - include_directories: [ project_inc, render_inc ], + include_directories: project_inc, c_args: project_c_args, cpp_args: project_cpp_args, link_args: render_link_args, @@ -434,7 +396,7 @@ if get_option('build_font') executable( 'font', sources: font_files, - include_directories: [ project_inc, font_inc ], + include_directories: project_inc, c_args: project_c_args, cpp_args: project_cpp_args, link_args: project_link_args, diff --git a/meson_options.txt b/meson_options.txt index cfffdafee..6cc15b608 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -18,10 +18,11 @@ option( description: 'Don\'t show notifications about available updates' ) option( - 'install_check', - type: 'boolean', - value: false, - description: 'Do install check on startup' + 'can_install', + type: 'combo', + choices: [ 'no', 'yes', 'yes_check', 'auto' ], + value: 'auto', + description: 'Disable (\'no\') or enable (\'yes\') setting up file and URL associations, or even offer to do it at startup (\'yes_check\')' ) option( 'http', @@ -29,12 +30,6 @@ option( value: true, description: 'Enable HTTP via libcurl' ) -option( - 'gravfft', - type: 'boolean', - value: true, - description: 'Enable FFT gravity via libfftw3' -) option( 'snapshot', type: 'boolean', @@ -207,6 +202,12 @@ option( value: true, description: 'Enforce encrypted HTTP traffic, may be disabled for debugging' ) +option( + 'secure_ciphers_only', + type: 'boolean', + value: false, + description: 'Use only secure ciphers for encrypted HTTP traffic, please review cipher list before enabling' +) option( 'prepare', type: 'boolean', diff --git a/resources/Info.template.plist b/resources/Info.template.plist index 835cc446c..0022bb960 100644 --- a/resources/Info.template.plist +++ b/resources/Info.template.plist @@ -18,7 +18,7 @@ CFBundleIconFile icon_exe.icns NSHumanReadableCopyright - Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2022 Simon Robertshaw, Copyright © 2016-2022 jacob1 + Copyright © 2008-2011 Stanislaw K Skowrenek, Copyright © 2011-2023 Simon Robertshaw, Copyright © 2016-2023 jacob1 CFBundleDevelopmentRegion English CFBundleDocumentTypes diff --git a/resources/font.bz2 b/resources/font.bz2 index a26ce218d..63da2c61a 100644 Binary files a/resources/font.bz2 and b/resources/font.bz2 differ diff --git a/resources/meson.build b/resources/meson.build index 48dd45de3..bf8b39532 100644 --- a/resources/meson.build +++ b/resources/meson.build @@ -60,6 +60,7 @@ if host_platform == 'windows' ], depend_files: [ 'resource.h', + 'winutf8.xml', ], ) elif host_platform == 'darwin' diff --git a/resources/powder-res.rc b/resources/powder-res.rc index a591330f8..7bb0c594e 100644 --- a/resources/powder-res.rc +++ b/resources/powder-res.rc @@ -1,4 +1,6 @@ #include "resource.h" +#include IDI_ICON ICON DISCARDABLE "icon_exe.ico" IDI_DOC_ICON ICON DISCARDABLE "icon_cps.ico" +CREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST "winutf8.xml" diff --git a/resources/resource.h b/resources/resource.h index d6086ac90..2c039a4af 100644 --- a/resources/resource.h +++ b/resources/resource.h @@ -1,5 +1,3 @@ -#define LUASCRIPT 256 - #define IDI_ICON 101 #define IDI_DOC_ICON 102 diff --git a/resources/winutf8.xml b/resources/winutf8.xml new file mode 100644 index 000000000..522749a1d --- /dev/null +++ b/resources/winutf8.xml @@ -0,0 +1,8 @@ + + + + + UTF-8 + + + diff --git a/src/Activity.h b/src/Activity.h index d11418c59..fa7c79294 100644 --- a/src/Activity.h +++ b/src/Activity.h @@ -1,6 +1,4 @@ #pragma once -#include "Config.h" - #include "gui/interface/Window.h" class Activity diff --git a/src/Config.template.h b/src/Config.template.h index b7c206970..9ef9dea16 100644 --- a/src/Config.template.h +++ b/src/Config.template.h @@ -1,158 +1,46 @@ -#ifndef CONFIG_H -#define CONFIG_H +#pragma once -#mesondefine CURL_STATICLIB -#mesondefine ZLIB_WINAPI +constexpr bool SET_WINDOW_ICON = @SET_WINDOW_ICON@; +constexpr bool DEBUG = @DEBUG@; +constexpr bool X86 = @X86@; +constexpr bool BETA = @BETA@; +constexpr bool SNAPSHOT = @SNAPSHOT@; +constexpr bool MOD = @MOD@; +constexpr bool NOHTTP = @NOHTTP@; +constexpr bool LUACONSOLE = @LUACONSOLE@; +constexpr bool ALLOW_FAKE_NEWER_VERSION = @ALLOW_FAKE_NEWER_VERSION@; +constexpr bool USE_UPDATESERVER = @USE_UPDATESERVER@; +constexpr bool CAN_INSTALL = @CAN_INSTALL@; +constexpr bool INSTALL_CHECK = @INSTALL_CHECK@; +constexpr bool IGNORE_UPDATES = @IGNORE_UPDATES@; +constexpr bool ENFORCE_HTTPS = @ENFORCE_HTTPS@; +constexpr bool SECURE_CIPHERS_ONLY = @SECURE_CIPHERS_ONLY@; +constexpr char PATH_SEP_CHAR = '@PATH_SEP_CHAR@'; -#mesondefine LUACONSOLE -#mesondefine NOHTTP -#mesondefine GRAVFFT -#mesondefine RENDERER -#mesondefine FONTEDITOR +constexpr char SERVER[] = "@SERVER@"; +constexpr char STATICSERVER[] = "@STATICSERVER@"; +constexpr char UPDATESERVER[] = "@UPDATESERVER@"; +constexpr char IDENT_PLATFORM[] = "@IDENT_PLATFORM@"; +constexpr char IDENT[] = "@IDENT@"; +constexpr char APPNAME[] = "@APPNAME@"; +constexpr char APPCOMMENT[] = "@APPCOMMENT@"; +constexpr char APPEXE[] = "@APPEXE@"; +constexpr char APPID[] = "@APPID@"; +constexpr char APPDATA[] = "@APPDATA@"; +constexpr char APPVENDOR[] = "@APPVENDOR@"; -#mesondefine BETA -#mesondefine DEBUG -#mesondefine IGNORE_UPDATES -#mesondefine LIN -#mesondefine AND -#mesondefine NO_INSTALL_CHECK -#mesondefine SNAPSHOT -#mesondefine WIN -#mesondefine MACOSX -#mesondefine X86 -#mesondefine X86_SSE -#mesondefine X86_SSE2 -#mesondefine X86_SSE3 -#mesondefine _64BIT -#mesondefine SERVER -#mesondefine STATICSERVER -#mesondefine UPDATESERVER -#mesondefine IDENT_PLATFORM -#mesondefine IDENT -#mesondefine ENFORCE_HTTPS -#define APPNAME "@APPNAME@" -#define APPCOMMENT "@APPCOMMENT@" -#define APPEXE "@APPEXE@" -#define APPID "@APPID@" -#define APPDATA "@APPDATA@" -#define APPVENDOR "@APPVENDOR@" +constexpr int SAVE_VERSION = 97; +constexpr int MINOR_VERSION = 0; +constexpr int BUILD_NUM = 352; +constexpr int SNAPSHOT_ID = @SNAPSHOT_ID@; +constexpr int MOD_ID = @MOD_ID@; +constexpr int FUTURE_SAVE_VERSION = 98; +constexpr int FUTURE_MINOR_VERSION = 0; -#ifdef WIN -# define PATH_SEP "\\" -# define PATH_SEP_CHAR '\\' -#else -# define PATH_SEP "/" -# define PATH_SEP_CHAR '/' -#endif +constexpr char IDENT_RELTYPE = SNAPSHOT ? 'S' : (BETA ? 'B' : 'R'); -//VersionInfoStart -#define SAVE_VERSION 97 -#define MINOR_VERSION 0 -#define BUILD_NUM 351 -#mesondefine SNAPSHOT_ID -#mesondefine MOD_ID -#define FUTURE_SAVE_VERSION 97 -#define FUTURE_MINOR_VERSION 0 - -#if !(defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0) -#undef FUTURE_SAVE_VERSION -#undef FUTURE_MINOR_VERSION -#endif -//VersionInfoEnd - -#if !(defined(MACOSX) && defined(DEBUG)) -#define HIGH_QUALITY_RESAMPLE //High quality image resampling, slower but much higher quality than my terribad linear interpolation -#endif - -#if defined(SNAPSHOT) -#define IDENT_RELTYPE "S" -#elif defined(BETA) -#define IDENT_RELTYPE "B" -#else -#define IDENT_RELTYPE "R" -#endif - -#if defined(X86_SSE3) -#define IDENT_BUILD "SSE3" -#elif defined(X86_SSE2) -#define IDENT_BUILD "SSE2" -#elif defined(X86_SSE) -#define IDENT_BUILD "SSE" -#else -#define IDENT_BUILD "NO" -#endif - -#define MTOS_EXPAND(str) #str -#define MTOS(str) MTOS_EXPAND(str) - -#define SCHEME "https://" -#define STATICSCHEME "https://" - -#define LOCAL_SAVE_DIR "Saves" - -#define STAMPS_DIR "stamps" - -#define BRUSH_DIR "Brushes" - -#ifndef M_GRAV -#define M_GRAV 6.67300e-1 -#endif - -#ifdef RENDERER -#define MENUSIZE 0 -#define BARSIZE 0 -#else -#define MENUSIZE 40 -#define BARSIZE 17 -#endif -#define XRES 612 -#define YRES 384 -#define NPART XRES*YRES - -#define XCNTR XRES/2 -#define YCNTR YRES/2 - -#define WINDOWW (XRES+BARSIZE) -#define WINDOWH (YRES+MENUSIZE) - -#define GRAV_DIFF - -#define MAXSIGNS 16 - -//CELL, the size of the pressure, gravity, and wall maps. Larger than 1 to prevent extreme lag -#define CELL 4 -#define ISTP (CELL/2) -#define CFDS (4.0f/CELL) -#define SIM_MAXVELOCITY 1e4f - -//Air constants -#define AIR_TSTEPP 0.3f -#define AIR_TSTEPV 0.4f -#define AIR_VADV 0.3f -#define AIR_VLOSS 0.999f -#define AIR_PLOSS 0.9999f - -#define NGOL 24 - -#define CIRCLE_BRUSH 0 -#define SQUARE_BRUSH 1 -#define TRI_BRUSH 2 -#define BRUSH_NUM 3 - -//Photon constants -#define SURF_RANGE 10 -#define NORMAL_MIN_EST 3 -#define NORMAL_INTERP 20 -#define NORMAL_FRAC 16 - -#define REFRACT 0x80000000 - -/* heavy flint glass, for awesome refraction/dispersion - this way you can make roof prisms easily */ -#define GLASS_IOR 1.9 -#define GLASS_DISP 0.07 - -#define SDEUT -#define R_TEMP 22 - -#endif /* CONFIG_H */ +constexpr char SCHEME[] = "https://"; +constexpr char STATICSCHEME[] = "https://"; +constexpr char LOCAL_SAVE_DIR[] = "Saves"; +constexpr char STAMPS_DIR[] = "stamps"; +constexpr char BRUSH_DIR[] = "Brushes"; diff --git a/src/Controller.h b/src/Controller.h index aae55b612..1b1e4841d 100644 --- a/src/Controller.h +++ b/src/Controller.h @@ -1,6 +1,4 @@ -#ifndef CONTROLLER_H_ -#define CONTROLLER_H_ - +#pragma once class Controller { private: @@ -9,5 +7,3 @@ private: virtual void Hide(); virtual ~Controller() = default; }; - -#endif /* CONTROLLER_H_ */ diff --git a/src/Format.cpp b/src/Format.cpp index 6bbcc4b30..1d79b98c4 100644 --- a/src/Format.cpp +++ b/src/Format.cpp @@ -1,19 +1,15 @@ -#include "Format.h" - +#include +#include +#include #include -#include #include #include -#include -#include -#include - +#include +#include +#include +#include "Format.h" #include "graphics/Graphics.h" -#ifndef RENDERER -# include "SDLCompat.h" -#endif - ByteString format::UnixtimeToDate(time_t unixtime, ByteString dateFormat) { struct tm * timeData; @@ -99,30 +95,187 @@ String format::CleanString(String dirtyString, bool ascii, bool color, bool newl return dirtyString; } -std::vector format::VideoBufferToPPM(const VideoBuffer & vidBuf) +std::vector format::PixelsToPPM(PlaneAdapter> const &input) { std::vector data; char buffer[256]; - sprintf(buffer, "P6\n%d %d\n255\n", vidBuf.Width, vidBuf.Height); - data.insert(data.end(), buffer, buffer+strlen(buffer)); + sprintf(buffer, "P6\n%d %d\n255\n", input.Size().X, input.Size().Y); + data.insert(data.end(), buffer, buffer + strlen(buffer)); - unsigned char * currentRow = new unsigned char[vidBuf.Width*3]; - for(int y = 0; y < vidBuf.Height; y++) + data.reserve(data.size() + input.Size().X * input.Size().Y * 3); + + for (int i = 0; i < input.Size().X * input.Size().Y; i++) { - int rowPos = 0; - for(int x = 0; x < vidBuf.Width; x++) - { - currentRow[rowPos++] = PIXR(vidBuf.Buffer[(y*vidBuf.Width)+x]); - currentRow[rowPos++] = PIXG(vidBuf.Buffer[(y*vidBuf.Width)+x]); - currentRow[rowPos++] = PIXB(vidBuf.Buffer[(y*vidBuf.Width)+x]); - } - data.insert(data.end(), currentRow, currentRow+(vidBuf.Width*3)); + auto colour = RGB::Unpack(input.data()[i]); + data.push_back(colour.Red); + data.push_back(colour.Green); + data.push_back(colour.Blue); } - delete [] currentRow; return data; } +static std::unique_ptr>> readPNG( + std::vector const &data, + // If omitted, + // RGB data is returned with A=0xFF + // RGBA data is returned as itself + // If specified + // RGB data is returned with A=0x00 + // RGBA data is blended against the background and returned with A=0x00 + std::optional> background +) +{ + png_infop info = nullptr; + auto deleter = [&info](png_struct *png) { + png_destroy_read_struct(&png, &info, NULL); + }; + auto png = std::unique_ptr( + png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, + [](png_structp png, png_const_charp msg) { + fprintf(stderr, "PNG error: %s\n", msg); + }, + [](png_structp png, png_const_charp msg) { + fprintf(stderr, "PNG warning: %s\n", msg); + } + ), deleter + ); + if (!png) + return nullptr; + + // libpng might longjmp() here in case of error + // Every time we create an object with a non-trivial destructor we must call setjmp again + if (setjmp(png_jmpbuf(png.get()))) + return nullptr; + + info = png_create_info_struct(png.get()); + if (!info) + return nullptr; + + auto it = data.begin(); + auto const end = data.end(); + auto readFn = [&it, end](png_structp png, png_bytep data, size_t length) { + if (size_t(end - it) < length) + png_error(png, "Tried to read beyond the buffer"); + std::copy_n(it, length, data); + it += length; + }; + + // See above + if (setjmp(png_jmpbuf(png.get()))) + return nullptr; + + png_set_read_fn(png.get(), static_cast(&readFn), [](png_structp png, png_bytep data, size_t length) { + (*static_cast(png_get_io_ptr(png)))(png, data, length); + }); + png_set_user_limits(png.get(), RES.X, RES.Y); // Refuse to parse larger images + png_read_info(png.get(), info); + + auto output = std::make_unique>>( + Vec2(png_get_image_width(png.get(), info), png_get_image_height(png.get(), info)) + ); + + std::vector rowPointers(output->Size().Y); + for (int y = 0; y < output->Size().Y; y++) + rowPointers[y] = reinterpret_cast(&*output->RowIterator(Vec2(0, y))); + + // See above + if (setjmp(png_jmpbuf(png.get()))) + return nullptr; + + png_set_filler(png.get(), background ? 0x00 : 0xFF, PNG_FILLER_AFTER); + png_set_bgr(png.get()); + + auto bitDepth = png_get_bit_depth(png.get(), info); + auto colorType = png_get_color_type(png.get(), info); + if (colorType == PNG_COLOR_TYPE_PALETTE) + png_set_palette_to_rgb(png.get()); + if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) + png_set_expand_gray_1_2_4_to_8(png.get()); + if (bitDepth == 16) + png_set_scale_16(png.get()); + if (png_get_valid(png.get(), info, PNG_INFO_tRNS)) + png_set_tRNS_to_alpha(png.get()); + if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_gray_to_rgb(png.get()); + if (background) + { + png_color_16 colour; + colour.red = background->Red; + colour.green = background->Green; + colour.blue = background->Blue; + png_set_background(png.get(), &colour, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); + } + + png_read_image(png.get(), rowPointers.data()); + return output; +} + +std::unique_ptr>> format::PixelsFromPNG(std::vector const &data) +{ + return readPNG(data, std::nullopt); +} + +std::unique_ptr>> format::PixelsFromPNG(std::vector const &data, RGB background) +{ + return readPNG(data, background); +} + +std::unique_ptr> format::PixelsToPNG(PlaneAdapter> const &input) +{ + png_infop info = nullptr; + auto deleter = [&info](png_struct *png) { + png_destroy_write_struct(&png, &info); + }; + auto png = std::unique_ptr( + png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, + [](png_structp png, png_const_charp msg) { + fprintf(stderr, "PNG error: %s\n", msg); + }, + [](png_structp png, png_const_charp msg) { + fprintf(stderr, "PNG warning: %s\n", msg); + } + ), deleter + ); + if (!png) + return nullptr; + + // libpng might longjmp() here in case of error + // Every time we create an object with a non-trivial destructor we must call setjmp again + if (setjmp(png_jmpbuf(png.get()))) + return nullptr; + + + info = png_create_info_struct(png.get()); + if (!info) + return nullptr; + + std::vector output; + auto writeFn = [&output](png_structp png, png_bytep data, size_t length) { + output.insert(output.end(), data, data + length); + }; + + std::vector rowPointers(input.Size().Y); + for (int y = 0; y < input.Size().Y; y++) + rowPointers[y] = reinterpret_cast(&*input.RowIterator(Vec2(0, y))); + + // See above + if (setjmp(png_jmpbuf(png.get()))) + return nullptr; + + png_set_write_fn(png.get(), static_cast(&writeFn), [](png_structp png, png_bytep data, size_t length) { + (*static_cast(png_get_io_ptr(png)))(png, data, length); + }, NULL); + png_set_IHDR(png.get(), info, input.Size().X, input.Size().Y, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + png_write_info(png.get(), info); + png_set_filler(png.get(), 0x00, PNG_FILLER_AFTER); + png_set_bgr(png.get()); + png_write_image(png.get(), const_cast(rowPointers.data())); + png_write_end(png.get(), NULL); + + return std::make_unique>(std::move(output)); +} + const static char hex[] = "0123456789ABCDEF"; ByteString format::URLEncode(ByteString source) diff --git a/src/Format.h b/src/Format.h index e395a673c..87c2aefab 100644 --- a/src/Format.h +++ b/src/Format.h @@ -1,8 +1,9 @@ #pragma once -#include "Config.h" - -#include "common/String.h" +#include #include +#include "common/String.h" +#include "common/Plane.h" +#include "graphics/Pixel.h" class VideoBuffer; @@ -13,7 +14,10 @@ namespace format ByteString UnixtimeToDate(time_t unixtime, ByteString dateFomat = ByteString("%d %b %Y")); ByteString UnixtimeToDateMini(time_t unixtime); String CleanString(String dirtyString, bool ascii, bool color, bool newlines, bool numeric = false); - std::vector VideoBufferToPPM(const VideoBuffer & vidBuf); + std::vector PixelsToPPM(PlaneAdapter> const &); + std::unique_ptr> PixelsToPNG(PlaneAdapter> const &); + std::unique_ptr>> PixelsFromPNG(std::vector const &); + std::unique_ptr>> PixelsFromPNG(std::vector const &, RGB background); void RenderTemperature(StringBuilder &sb, float temp, int scale); float StringToTemperature(String str, int defaultScale); } diff --git a/src/Misc.cpp b/src/Misc.cpp index 5b4c86212..668f94bbc 100644 --- a/src/Misc.cpp +++ b/src/Misc.cpp @@ -1,140 +1,10 @@ #include "Misc.h" - -#include "Config.h" - +#include "common/tpt-minmax.h" +#include "common/String.h" #include #include #include -#include "common/tpt-minmax.h" -#include "common/String.h" - -const static char hex[] = "0123456789ABCDEF"; -void strcaturl(char *dst, char *src) -{ - char *d; - unsigned char *s; - - for (d=dst; *d; d++) ; - - for (s=(unsigned char *)src; *s; s++) - { - if ((*s>='0' && *s<='9') || - (*s>='a' && *s<='z') || - (*s>='A' && *s<='Z')) - *(d++) = *s; - else - { - *(d++) = '%'; - *(d++) = hex[*s>>4]; - *(d++) = hex[*s&15]; - } - } - *d = 0; -} - -void strappend(char *dst, const char *src) -{ - char *d; - unsigned char *s; - - for (d=dst; *d; d++) ; - - for (s=(unsigned char *)src; *s; s++) - { - *(d++) = *s; - } - *d = 0; -} - -void *file_load(char *fn, int *size) -{ - FILE *f = fopen(fn, "rb"); - void *s; - - if (!f) - return NULL; - fseek(f, 0, SEEK_END); - *size = ftell(f); - fseek(f, 0, SEEK_SET); - s = malloc(*size); - if (!s) - { - fclose(f); - return NULL; - } - int readsize = fread(s, *size, 1, f); - fclose(f); - if (readsize != 1) - { - free(s); - return NULL; - } - return s; -} - -matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2) -{ - matrix2d result = { - m1.a*m2.a+m1.b*m2.c, m1.a*m2.b+m1.b*m2.d, - m1.c*m2.a+m1.d*m2.c, m1.c*m2.b+m1.d*m2.d - }; - return result; -} -vector2d m2d_multiply_v2d(matrix2d m, vector2d v) -{ - vector2d result = { - m.a*v.x+m.b*v.y, - m.c*v.x+m.d*v.y - }; - return result; -} -matrix2d m2d_multiply_float(matrix2d m, float s) -{ - matrix2d result = { - m.a*s, m.b*s, - m.c*s, m.d*s, - }; - return result; -} - -vector2d v2d_multiply_float(vector2d v, float s) -{ - vector2d result = { - v.x*s, - v.y*s - }; - return result; -} - -vector2d v2d_add(vector2d v1, vector2d v2) -{ - vector2d result = { - v1.x+v2.x, - v1.y+v2.y - }; - return result; -} -vector2d v2d_sub(vector2d v1, vector2d v2) -{ - vector2d result = { - v1.x-v2.x, - v1.y-v2.y - }; - return result; -} - -matrix2d m2d_new(float me0, float me1, float me2, float me3) -{ - matrix2d result = {me0,me1,me2,me3}; - return result; -} -vector2d v2d_new(float x, float y) -{ - vector2d result = {x, y}; - return result; -} - void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b)//convert 0-255(0-360 for H) HSV values to 0-255 RGB { float hh, ss, vv, c, x; @@ -216,9 +86,6 @@ void membwand(void * destv, void * srcv, size_t destsize, size_t srcsize) } } -vector2d v2d_zero = {0,0}; -matrix2d m2d_identity = {1,0,0,1}; - bool byteStringEqualsString(const ByteString &str, const char *data, size_t size) { return str.size() == size && !memcmp(str.data(), data, size); diff --git a/src/Misc.h b/src/Misc.h index 02a7ed589..5faa307fb 100644 --- a/src/Misc.h +++ b/src/Misc.h @@ -1,12 +1,29 @@ -#ifndef UTILS_H -#define UTILS_H -#include "Config.h" +#pragma once #include #include #include #include #include +template +inline std::pair floorDiv(Signed a, Signed b) +{ + auto quo = a / b; + auto rem = a % b; + if (a < Signed(0) && rem) + { + quo -= Signed(1); + rem += b; + } + return { quo, rem }; +} + +template +inline std::pair ceilDiv(Signed a, Signed b) +{ + return floorDiv(a + b - Signed(1), b); +} + //Linear interpolation template inline T LinearInterpolate(T val1, T val2, T lowerCoord, T upperCoord, T coord) { @@ -55,52 +72,10 @@ inline float restrict_flt(float f, float min, float max) return f; } -void save_presets(int do_update); - -void load_presets(void); - -void strcaturl(char *dst, const char *src); - -void strappend(char *dst, const char *src); - -void *file_load(char *fn, int *size); - -extern char *clipboard_text; - void HSV_to_RGB(int h,int s,int v,int *r,int *g,int *b); - void RGB_to_HSV(int r,int g,int b,int *h,int *s,int *v); - void membwand(void * dest, void * src, size_t destsize, size_t srcsize); -// a b -// c d - -struct matrix2d { - float a,b,c,d; -}; -typedef struct matrix2d matrix2d; - -// column vector -struct vector2d { - float x,y; -}; -typedef struct vector2d vector2d; - -matrix2d m2d_multiply_m2d(matrix2d m1, matrix2d m2); -vector2d m2d_multiply_v2d(matrix2d m, vector2d v); -matrix2d m2d_multiply_float(matrix2d m, float s); -vector2d v2d_multiply_float(vector2d v, float s); - -vector2d v2d_add(vector2d v1, vector2d v2); -vector2d v2d_sub(vector2d v1, vector2d v2); - -matrix2d m2d_new(float me0, float me1, float me2, float me3); -vector2d v2d_new(float x, float y); - -extern vector2d v2d_zero; -extern matrix2d m2d_identity; - class ByteString; bool byteStringEqualsString(const ByteString &str, const char *data, size_t size); @@ -110,5 +85,3 @@ bool byteStringEqualsLiteral(const ByteString &str, const char (&lit)[N]) { return byteStringEqualsString(str, lit, N - 1U); } - -#endif diff --git a/src/PowderToy.cpp b/src/PowderToy.cpp new file mode 100644 index 000000000..897370a0a --- /dev/null +++ b/src/PowderToy.cpp @@ -0,0 +1,534 @@ +#include "PowderToySDL.h" +#include "Format.h" +#include "X86KillDenormals.h" +#include "prefs/GlobalPrefs.h" +#include "client/Client.h" +#include "client/GameSave.h" +#include "client/SaveFile.h" +#include "client/SaveInfo.h" +#include "client/http/requestmanager/RequestManager.h" +#include "client/http/GetSaveRequest.h" +#include "client/http/GetSaveDataRequest.h" +#include "common/platform/Platform.h" +#include "graphics/Graphics.h" +#include "simulation/SaveRenderer.h" +#include "common/tpt-rand.h" +#include "gui/game/Favorite.h" +#include "gui/Style.h" +#include "gui/game/GameController.h" +#include "gui/game/GameView.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "gui/dialogues/ErrorMessage.h" +#include "gui/interface/Engine.h" +#include "Config.h" +#include "SimulationConfig.h" +#include +#include +#include +#include +#include + +void LoadWindowPosition() +{ + if (Client::Ref().IsFirstRun()) + { + return; + } + + auto &prefs = GlobalPrefs::Ref(); + int savedWindowX = prefs.Get("WindowX", INT_MAX); + int savedWindowY = prefs.Get("WindowY", INT_MAX); + + int borderTop, borderLeft; + SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr); + // Sometimes (Windows), the border size may not be reported for 200+ frames + // So just have a default of 5 to ensure the window doesn't get stuck where it can't be moved + if (borderTop == 0) + borderTop = 5; + + int numDisplays = SDL_GetNumVideoDisplays(); + SDL_Rect displayBounds; + bool ok = false; + for (int i = 0; i < numDisplays; i++) + { + SDL_GetDisplayBounds(i, &displayBounds); + if (savedWindowX + borderTop > displayBounds.x && savedWindowY + borderLeft > displayBounds.y && + savedWindowX + borderTop < displayBounds.x + displayBounds.w && + savedWindowY + borderLeft < displayBounds.y + displayBounds.h) + { + ok = true; + break; + } + } + if (ok) + SDL_SetWindowPosition(sdl_window, savedWindowX + borderLeft, savedWindowY + borderTop); +} + +void SaveWindowPosition() +{ + int x, y; + SDL_GetWindowPosition(sdl_window, &x, &y); + + int borderTop, borderLeft; + SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr); + + auto &prefs = GlobalPrefs::Ref(); + prefs.Set("WindowX", x - borderLeft); + prefs.Set("WindowY", y - borderTop); +} + +void LargeScreenDialog() +{ + StringBuilder message; + message << "Switching to " << scale << "x size mode since your screen was determined to be large enough: "; + message << desktopWidth << "x" << desktopHeight << " detected, " << WINDOWW*scale << "x" << WINDOWH*scale << " required"; + message << "\nTo undo this, hit Cancel. You can change this in settings at any time."; + if (!ConfirmPrompt::Blocking("Large screen detected", message.Build())) + { + GlobalPrefs::Ref().Set("Scale", 1); + ui::Engine::Ref().SetScale(1); + } +} + +void TickClient() +{ + Client::Ref().Tick(); +} + +void BlueScreen(String detailMessage) +{ + auto &engine = ui::Engine::Ref(); + engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2)); + + String errorTitle = "ERROR"; + String errorDetails = "Details: " + detailMessage; + String errorHelp = String("An unrecoverable fault has occurred, please report the error by visiting the website below\n") + SCHEME + SERVER; + + // We use the width of errorHelp to center, but heights of the individual texts for vertical spacing + auto pos = engine.g->Size() / 2 - Vec2(Graphics::TextSize(errorHelp).X / 2, 100); + engine.g->BlendText(pos, errorTitle, 0xFFFFFF_rgb .WithAlpha(0xFF)); + pos.Y += 4 + Graphics::TextSize(errorTitle).Y; + engine.g->BlendText(pos, errorDetails, 0xFFFFFF_rgb .WithAlpha(0xFF)); + pos.Y += 4 + Graphics::TextSize(errorDetails).Y; + engine.g->BlendText(pos, errorHelp, 0xFFFFFF_rgb .WithAlpha(0xFF)); + + //Death loop + SDL_Event event; + while(true) + { + while (SDL_PollEvent(&event)) + if(event.type == SDL_QUIT) + exit(-1); // Don't use Platform::Exit, we're practically zombies at this point anyway. + blit(engine.g->Data()); + } +} + +struct +{ + int sig; + const char *message; +} signalMessages[] = { + { SIGSEGV, "Memory read/write error" }, + { SIGFPE, "Floating point exception" }, + { SIGILL, "Program execution exception" }, + { SIGABRT, "Unexpected program abort" }, + { 0, nullptr }, +}; + +void SigHandler(int signal) +{ + const char *message = "Unknown signal"; + for (auto *msg = signalMessages; msg->message; ++msg) + { + if (msg->sig == signal) + { + message = msg->message; + break; + } + } + BlueScreen(ByteString(message).FromUtf8()); +} + +constexpr int SCALE_MAXIMUM = 10; +constexpr int SCALE_MARGIN = 30; + +int GuessBestScale() +{ + const int widthNoMargin = desktopWidth - SCALE_MARGIN; + const int widthGuess = widthNoMargin / WINDOWW; + + const int heightNoMargin = desktopHeight - SCALE_MARGIN; + const int heightGuess = heightNoMargin / WINDOWH; + + int guess = std::min(widthGuess, heightGuess); + if(guess < 1 || guess > SCALE_MAXIMUM) + guess = 1; + + return guess; +} + +struct ExplicitSingletons +{ + // These need to be listed in the order they are populated in main. + std::unique_ptr globalPrefs; + http::RequestManagerPtr requestManager; + std::unique_ptr client; + std::unique_ptr saveRenderer; + std::unique_ptr favorite; + std::unique_ptr engine; + std::unique_ptr gameController; +}; +static std::unique_ptr explicitSingletons; + +int main(int argc, char * argv[]) +{ + Platform::SetupCrt(); + Platform::Atexit([]() { + // Unregister dodgy error handlers so they don't try to show the blue screen when the window is closed + for (auto *msg = signalMessages; msg->message; ++msg) + { + signal(msg->sig, SIG_DFL); + } + SDLClose(); + explicitSingletons.reset(); + }); + explicitSingletons = std::make_unique(); + + + // https://bugzilla.libsdl.org/show_bug.cgi?id=3796 + if (SDL_Init(0) < 0) + { + fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError()); + return 1; + } + + Platform::originalCwd = Platform::GetCwd(); + + using Argument = std::optional; + std::map arguments; + + for (auto i = 1; i < argc; ++i) + { + auto str = ByteString(argv[i]); + if (str.BeginsWith("file://")) + { + arguments.insert({ "open", format::URLDecode(str.substr(7 /* length of the "file://" prefix */)) }); + } + else if (str.BeginsWith("ptsave:")) + { + arguments.insert({ "ptsave", str }); + } + else if (auto split = str.SplitBy(':')) + { + arguments.insert({ split.Before(), split.After() }); + } + else if (auto split = str.SplitBy('=')) + { + arguments.insert({ split.Before(), split.After() }); + } + else if (str == "open" || str == "ptsave" || str == "ddir") + { + if (i + 1 < argc) + { + arguments.insert({ str, argv[i + 1] }); + i += 1; + } + else + { + std::cerr << "no value provided for command line parameter " << str << std::endl; + } + } + else + { + arguments.insert({ str, "" }); // so .has_value() is true + } + } + + auto ddirArg = arguments["ddir"]; + if (ddirArg.has_value()) + { + if (Platform::ChangeDir(ddirArg.value())) + Platform::sharedCwd = Platform::GetCwd(); + else + perror("failed to chdir to requested ddir"); + } + else + { + auto ddir = std::unique_ptr(SDL_GetPrefPath(NULL, APPDATA), SDL_free); + if (!Platform::FileExists("powder.pref")) + { + if (ddir) + { + if (!Platform::ChangeDir(ddir.get())) + { + perror("failed to chdir to default ddir"); + ddir.reset(); + } + } + } + + if (ddir) + { + Platform::sharedCwd = ddir.get(); + } + } + // We're now in the correct directory, time to get prefs. + explicitSingletons->globalPrefs = std::make_unique(); + + auto &prefs = GlobalPrefs::Ref(); + scale = prefs.Get("Scale", 1); + auto graveExitsConsole = prefs.Get("GraveExitsConsole", true); + resizable = prefs.Get("Resizable", false); + fullscreen = prefs.Get("Fullscreen", false); + altFullscreen = prefs.Get("AltFullscreen", false); + forceIntegerScaling = prefs.Get("ForceIntegerScaling", true); + momentumScroll = prefs.Get("MomentumScroll", true); + showAvatars = prefs.Get("ShowAvatars", true); + + auto true_string = [](ByteString str) { + str = str.ToLower(); + return str == "true" || + str == "t" || + str == "on" || + str == "yes" || + str == "y" || + str == "1" || + str == ""; // standalone "redirect" or "disable-bluescreen" or similar arguments + }; + auto true_arg = [&true_string](Argument arg) { + return arg.has_value() && true_string(arg.value()); + }; + + auto kioskArg = arguments["kiosk"]; + if (kioskArg.has_value()) + { + fullscreen = true_string(kioskArg.value()); + prefs.Set("Fullscreen", fullscreen); + } + + if (true_arg(arguments["redirect"])) + { + FILE *new_stdout = freopen("stdout.log", "w", stdout); + FILE *new_stderr = freopen("stderr.log", "w", stderr); + if (!new_stdout || !new_stderr) + { + Platform::Exit(42); + } + } + + auto scaleArg = arguments["scale"]; + if (scaleArg.has_value()) + { + try + { + scale = scaleArg.value().ToNumber(); + prefs.Set("Scale", scale); + } + catch (const std::runtime_error &e) + { + std::cerr << "failed to set scale: " << e.what() << std::endl; + } + } + + auto clientConfig = [&prefs](Argument arg, ByteString name, ByteString defaultValue) { + ByteString value; + if (arg.has_value()) + { + value = arg.value(); + if (value == "") + { + value = defaultValue; + } + prefs.Set(name, value); + } + else + { + value = prefs.Get(name, defaultValue); + } + return value; + }; + ByteString proxyString = clientConfig(arguments["proxy"], "Proxy", ""); + ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", ""); + ByteString capathString = clientConfig(arguments["capath"], "CAPath", ""); + bool disableNetwork = true_arg(arguments["disable-network"]); + explicitSingletons->requestManager = http::RequestManager::Create(proxyString, cafileString, capathString, disableNetwork); + + explicitSingletons->client = std::make_unique(); + Client::Ref().Initialize(); + + explicitSingletons->saveRenderer = std::make_unique(); + explicitSingletons->favorite = std::make_unique(); + explicitSingletons->engine = std::make_unique(); + + // TODO: maybe bind the maximum allowed scale to screen size somehow + if(scale < 1 || scale > SCALE_MAXIMUM) + scale = 1; + + SDLOpen(); + + if (Client::Ref().IsFirstRun()) + { + scale = GuessBestScale(); + if (scale > 1) + { + prefs.Set("Scale", scale); + SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale); + showLargeScreenDialog = true; + } + } + + StopTextInput(); + + auto &engine = ui::Engine::Ref(); + engine.g = new Graphics(); + engine.Scale = scale; + engine.GraveExitsConsole = graveExitsConsole; + engine.SetResizable(resizable); + engine.Fullscreen = fullscreen; + engine.SetAltFullscreen(altFullscreen); + engine.SetForceIntegerScaling(forceIntegerScaling); + engine.MomentumScroll = momentumScroll; + engine.ShowAvatars = showAvatars; + engine.Begin(); + engine.SetFastQuit(prefs.Get("FastQuit", true)); + + bool enableBluescreen = !DEBUG && !true_arg(arguments["disable-bluescreen"]); + if (enableBluescreen) + { + //Get ready to catch any dodgy errors + for (auto *msg = signalMessages; msg->message; ++msg) + { + signal(msg->sig, SigHandler); + } + } + + if constexpr (X86) + { + X86KillDenormals(); + } + + auto wrapWithBluescreen = [&]() { + explicitSingletons->gameController = std::make_unique(); + auto *gameController = explicitSingletons->gameController.get(); + engine.ShowWindow(gameController->GetView()); + + auto openArg = arguments["open"]; + if (openArg.has_value()) + { + if constexpr (DEBUG) + { + std::cout << "Loading " << openArg.value() << std::endl; + } + if (Platform::FileExists(openArg.value())) + { + try + { + std::vector gameSaveData; + if (!Platform::ReadFile(gameSaveData, openArg.value())) + { + new ErrorMessage("Error", "Could not read file"); + } + else + { + auto newFile = std::make_unique(openArg.value()); + auto newSave = std::make_unique(std::move(gameSaveData)); + newFile->SetGameSave(std::move(newSave)); + gameController->LoadSaveFile(std::move(newFile)); + } + + } + catch (std::exception & e) + { + new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ; + } + } + else + { + new ErrorMessage("Error", "Could not open file"); + } + } + + auto ptsaveArg = arguments["ptsave"]; + if (ptsaveArg.has_value()) + { + engine.g->Clear(); + engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb); + String loadingText = "Loading save..."; + engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle); + + blit(engine.g->Data()); + try + { + ByteString saveIdPart; + if (ByteString::Split split = ptsaveArg.value().SplitBy(':')) + { + if (split.Before() != "ptsave") + throw std::runtime_error("Not a ptsave link"); + saveIdPart = split.After().SplitBy('#').Before(); + } + else + throw std::runtime_error("Invalid save link"); + + if (!saveIdPart.size()) + throw std::runtime_error("No Save ID"); + if constexpr (DEBUG) + { + std::cout << "Got Ptsave: id: " << saveIdPart << std::endl; + } + int saveId = saveIdPart.ToNumber(); + + auto getSave = std::make_unique(saveId, 0); + getSave->Start(); + getSave->Wait(); + std::unique_ptr newSave; + try + { + newSave = getSave->Finish(); + } + catch (const http::RequestError &ex) + { + throw std::runtime_error("Could not load save info\n" + ByteString(ex.what())); + } + auto getSaveData = std::make_unique(saveId, 0); + getSaveData->Start(); + getSaveData->Wait(); + std::unique_ptr saveData; + try + { + saveData = std::make_unique(getSaveData->Finish()); + } + catch (const http::RequestError &ex) + { + throw std::runtime_error("Could not load save\n" + ByteString(ex.what())); + } + newSave->SetGameSave(std::move(saveData)); + gameController->LoadSave(std::move(newSave)); + } + catch (std::exception & e) + { + new ErrorMessage("Error", ByteString(e.what()).FromUtf8()); + } + } + + EngineProcess(); + SaveWindowPosition(); + }; + + if (enableBluescreen) + { + try + { + wrapWithBluescreen(); + } + catch (const std::exception &e) + { + BlueScreen(ByteString(e.what()).FromUtf8()); + } + } + else + { + wrapWithBluescreen(); + } + Platform::Exit(0); + return 0; +} diff --git a/src/PowderToy.h b/src/PowderToy.h deleted file mode 100644 index f59e8f58b..000000000 --- a/src/PowderToy.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "common/String.h" - -void EngineProcess(); -void ClipboardPush(ByteString text); -ByteString ClipboardPull(); -int GetModifiers(); -unsigned int GetTicks(); -void StartTextInput(); -void StopTextInput(); -void SetTextInputRect(int x, int y, int w, int h); diff --git a/src/PowderToyFontEditor.cpp b/src/PowderToyFontEditor.cpp index cb7a53e83..a4f140805 100644 --- a/src/PowderToyFontEditor.cpp +++ b/src/PowderToyFontEditor.cpp @@ -1,442 +1,45 @@ -#include "Config.h" -#include -#include -#ifdef WIN -#include -#endif -#include "SDLCompat.h" - -#ifdef X86_SSE -#include -#endif -#ifdef X86_SSE3 -#include -#endif - -#include -#if defined(LIN) -# include "icon_exe.png.h" -#endif -#include - -#ifndef WIN -#include -#endif -#ifdef MACOSX -# include "common/macosx.h" -#endif - -#include "Format.h" -#include "Misc.h" - +#include "PowderToySDL.h" #include "graphics/Graphics.h" - -#include "client/SaveInfo.h" -#include "client/GameSave.h" -#include "client/SaveFile.h" - -#include "gui/game/GameController.h" -#include "gui/game/GameView.h" +#include "common/platform/Platform.h" +#include "common/tpt-rand.h" #include "gui/font/FontEditor.h" -#include "gui/dialogues/ErrorMessage.h" -#include "gui/dialogues/ConfirmPrompt.h" -#include "gui/interface/Keys.h" -#include "gui/Style.h" #include "gui/interface/Engine.h" +#include "Config.h" +#include "SimulationConfig.h" +#include +#include -#include "SDLCompat.h" - -int desktopWidth = 1280, desktopHeight = 1024; - -SDL_Window * sdl_window; -SDL_Renderer * sdl_renderer; -SDL_Texture * sdl_texture; -int scale = 1; -bool fullscreen = false; -bool altFullscreen = false; -bool forceIntegerScaling = true; -bool resizable = false; - - -void StartTextInput() +void LoadWindowPosition() { - SDL_StartTextInput(); } -void StopTextInput() +void SaveWindowPosition() { - SDL_StopTextInput(); } -void SetTextInputRect(int x, int y, int w, int h) +void LargeScreenDialog() { - SDL_Rect rect; - rect.x = x; - rect.y = y; - rect.w = w; - rect.h = h; - SDL_SetTextInputRect(&rect); } -void ClipboardPush(ByteString text) +void TickClient() { - SDL_SetClipboardText(text.c_str()); } -ByteString ClipboardPull() +struct ExplicitSingletons { - return ByteString(SDL_GetClipboardText()); -} - -int GetModifiers() -{ - return SDL_GetModState(); -} - -void CalculateMousePosition(int *x, int *y) -{ - int globalMx, globalMy; - SDL_GetGlobalMouseState(&globalMx, &globalMy); - int windowX, windowY; - SDL_GetWindowPosition(sdl_window, &windowX, &windowY); - - if (x) - *x = (globalMx - windowX) / scale; - if (y) - *y = (globalMy - windowY) / scale; -} - -void blit(pixel * vid) -{ - SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32)); - // need to clear the renderer if there are black edges (fullscreen, or resizable window) - if (fullscreen || resizable) - SDL_RenderClear(sdl_renderer); - SDL_RenderCopy(sdl_renderer, sdl_texture, NULL, NULL); - SDL_RenderPresent(sdl_renderer); -} - -void RecreateWindow(); -int SDLOpen() -{ - if (SDL_Init(SDL_INIT_VIDEO) < 0) - { - fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError()); - return 1; - } - - RecreateWindow(); - - int displayIndex = SDL_GetWindowDisplayIndex(sdl_window); - if (displayIndex >= 0) - { - SDL_Rect rect; - if (!SDL_GetDisplayUsableBounds(displayIndex, &rect)) - { - desktopWidth = rect.w; - desktopHeight = rect.h; - } - } - -#ifdef LIN - std::vector imageData; - int imgw, imgh; - if (PngDataToPixels(imageData, imgw, imgh, reinterpret_cast(icon_exe_png), icon_exe_png_size, false)) - { - SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(&imageData[0], imgw, imgh, 32, imgw * sizeof(pixel), 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); - SDL_SetWindowIcon(sdl_window, icon); - SDL_FreeSurface(icon); - } -#endif - - return 0; -} - -void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_) -{ -// bool changingScale = scale != scale_; - bool changingFullscreen = fullscreen_ != fullscreen || (altFullscreen_ != altFullscreen && fullscreen); - bool changingResizable = resizable != resizable_; - scale = scale_; - fullscreen = fullscreen_; - altFullscreen = altFullscreen_; - resizable = resizable_; - forceIntegerScaling = forceIntegerScaling_; - // Recreate the window when toggling fullscreen, due to occasional issues - // Also recreate it when enabling resizable windows, to fix bugs on windows, - // see https://github.com/jacob1/The-Powder-Toy/issues/24 - if (changingFullscreen || (changingResizable && resizable && !fullscreen)) - { - RecreateWindow(); - return; - } - if (changingResizable) - SDL_RestoreWindow(sdl_window); - - SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale); - SDL_RenderSetIntegerScale(sdl_renderer, forceIntegerScaling && fullscreen ? SDL_TRUE : SDL_FALSE); - unsigned int flags = 0; - if (fullscreen) - flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP; - SDL_SetWindowFullscreen(sdl_window, flags); - if (fullscreen) - SDL_RaiseWindow(sdl_window); - SDL_SetWindowResizable(sdl_window, resizable ? SDL_TRUE : SDL_FALSE); -} - -void RecreateWindow() -{ - unsigned int flags = 0; - if (fullscreen) - flags = altFullscreen ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP; - if (resizable && !fullscreen) - flags |= SDL_WINDOW_RESIZABLE; - - if (sdl_texture) - SDL_DestroyTexture(sdl_texture); - if (sdl_renderer) - SDL_DestroyRenderer(sdl_renderer); - if (sdl_window) - { - SDL_DestroyWindow(sdl_window); - } - - sdl_window = SDL_CreateWindow(APPNAME, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, WINDOWW * scale, WINDOWH * scale, - flags); - sdl_renderer = SDL_CreateRenderer(sdl_window, -1, 0); - SDL_RenderSetLogicalSize(sdl_renderer, WINDOWW, WINDOWH); - if (forceIntegerScaling && fullscreen) - SDL_RenderSetIntegerScale(sdl_renderer, SDL_TRUE); - sdl_texture = SDL_CreateTexture(sdl_renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, WINDOWW, WINDOWH); - SDL_RaiseWindow(sdl_window); - SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); - //Uncomment this to enable resizing - //SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - //SDL_SetWindowResizable(sdl_window, SDL_TRUE); -} - -unsigned int GetTicks() -{ - return SDL_GetTicks(); -} - -int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0; -unsigned int lastTick = 0; -unsigned int lastFpsUpdate = 0; -float fps = 0; -ui::Engine * engine = NULL; -bool showDoubleScreenDialog = false; -float currentWidth, currentHeight; - -int mousex = 0, mousey = 0; -int mouseButton = 0; -bool mouseDown = false; - -bool calculatedInitialMouse = false, delay = false; -bool hasMouseMoved = false; - -void EventProcess(SDL_Event event) -{ - switch (event.type) - { - case SDL_QUIT: - if (engine->GetFastQuit() || engine->CloseWindow()) - engine->Exit(); - break; - case SDL_KEYDOWN: - if (SDL_GetModState() & KMOD_GUI) - { - break; - } - if (!event.key.repeat && event.key.keysym.sym == 'q' && (event.key.keysym.mod&KMOD_CTRL)) - engine->ConfirmExit(); - else - engine->onKeyPress(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT); - break; - case SDL_KEYUP: - if (SDL_GetModState() & KMOD_GUI) - { - break; - } - engine->onKeyRelease(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT); - break; - case SDL_TEXTINPUT: - if (SDL_GetModState() & KMOD_GUI) - { - break; - } - engine->onTextInput(ByteString(event.text.text).FromUtf8()); - break; - case SDL_TEXTEDITING: - if (SDL_GetModState() & KMOD_GUI) - { - break; - } - engine->onTextEditing(ByteString(event.edit.text).FromUtf8(), event.edit.start); - break; - case SDL_MOUSEWHEEL: - { - // int x = event.wheel.x; - int y = event.wheel.y; - if (event.wheel.direction == SDL_MOUSEWHEEL_FLIPPED) - { - // x *= -1; - y *= -1; - } - - engine->onMouseWheel(mousex, mousey, y); // TODO: pass x? - break; - } - case SDL_MOUSEMOTION: - mousex = event.motion.x; - mousey = event.motion.y; - engine->onMouseMove(mousex, mousey); - - hasMouseMoved = true; - break; - case SDL_DROPFILE: - engine->onFileDrop(event.drop.file); - SDL_free(event.drop.file); - break; - case SDL_MOUSEBUTTONDOWN: - // if mouse hasn't moved yet, sdl will send 0,0. We don't want that - if (hasMouseMoved) - { - mousex = event.motion.x; - mousey = event.motion.y; - } - mouseButton = event.button.button; - engine->onMouseClick(event.motion.x, event.motion.y, mouseButton); - - mouseDown = true; -#if !defined(NDEBUG) && !defined(DEBUG) - SDL_CaptureMouse(SDL_TRUE); -#endif - break; - case SDL_MOUSEBUTTONUP: - // if mouse hasn't moved yet, sdl will send 0,0. We don't want that - if (hasMouseMoved) - { - mousex = event.motion.x; - mousey = event.motion.y; - } - mouseButton = event.button.button; - engine->onMouseUnclick(mousex, mousey, mouseButton); - - mouseDown = false; -#if !defined(NDEBUG) && !defined(DEBUG) - SDL_CaptureMouse(SDL_FALSE); -#endif - break; - case SDL_WINDOWEVENT: - { - switch (event.window.event) - { - case SDL_WINDOWEVENT_SHOWN: - if (!calculatedInitialMouse) - { - //initial mouse coords, sdl won't tell us this if mouse hasn't moved - CalculateMousePosition(&mousex, &mousey); - engine->onMouseMove(mousex, mousey); - calculatedInitialMouse = true; - } - break; - // This event would be needed in certain glitchy cases of window resizing - // But for all currently tested cases, it isn't needed - /*case SDL_WINDOWEVENT_RESIZED: - { - float width = event.window.data1; - float height = event.window.data2; - - currentWidth = width; - currentHeight = height; - // this "* scale" thing doesn't really work properly - // currently there is a bug where input doesn't scale properly after resizing, only when double scale mode is active - inputScaleH = (float)WINDOWW * scale / currentWidth; - inputScaleV = (float)WINDOWH * scale / currentHeight; - std::cout << "Changing input scale to " << inputScaleH << "x" << inputScaleV << std::endl; - break; - }*/ - // This would send a mouse up event when focus is lost - // Not even sdl itself will know when the mouse was released if it happens in another window - // So it will ignore the next mouse down (after tpt is re-focused) and not send any events at all - // This is more unintuitive than pretending the mouse is still down when it's not, so this code is commented out - /*case SDL_WINDOWEVENT_FOCUS_LOST: - if (mouseDown) - { - mouseDown = false; - engine->onMouseUnclick(mousex, mousey, mouseButton); - } - break;*/ - } - break; - } - } -} - -void EngineProcess() -{ - double frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f; - SDL_Event event; - while(engine->Running()) - { - int frameStart = SDL_GetTicks(); - if(engine->Broken()) { engine->UnBreak(); break; } - event.type = 0; - while (SDL_PollEvent(&event)) - { - EventProcess(event); - event.type = 0; //Clear last event - } - if(engine->Broken()) { engine->UnBreak(); break; } - - engine->Tick(); - engine->Draw(); - - if (scale != engine->Scale || fullscreen != engine->Fullscreen || - altFullscreen != engine->GetAltFullscreen() || - forceIntegerScaling != engine->GetForceIntegerScaling() || resizable != engine->GetResizable()) - { - SDLSetScreen(engine->Scale, engine->GetResizable(), engine->Fullscreen, engine->GetAltFullscreen(), - engine->GetForceIntegerScaling()); - } - - blit(engine->g->vid); - - int frameTime = SDL_GetTicks() - frameStart; - frameTimeAvg = frameTimeAvg * 0.8 + frameTime * 0.2; - float fpsLimit = ui::Engine::Ref().FpsLimit; - if(fpsLimit > 2) - { - double offset = 1000.0 / fpsLimit - frameTimeAvg; - if(offset > 0) - SDL_Delay(Uint32(offset + 0.5)); - } - int correctedFrameTime = SDL_GetTicks() - frameStart; - correctedFrameTimeAvg = correctedFrameTimeAvg * 0.95 + correctedFrameTime * 0.05; - if (frameStart - lastFpsUpdate > 200) - { - engine->SetFps(1000.0 / correctedFrameTimeAvg); - lastFpsUpdate = frameStart; - } - if (frameStart - lastTick > 100) - { - lastTick = frameStart; - } - if (showDoubleScreenDialog) - { - showDoubleScreenDialog = false; - } - } -#ifdef DEBUG - std::cout << "Breaking out of EngineProcess" << std::endl; -#endif -} + // These need to be listed in the order they are populated in main. + std::unique_ptr engine; +}; +static std::unique_ptr explicitSingletons; int main(int argc, char * argv[]) { - currentWidth = WINDOWW; - currentHeight = WINDOWH; + Platform::SetupCrt(); + Platform::Atexit([]() { + SDLClose(); + explicitSingletons.reset(); + }); + explicitSingletons = std::make_unique(); scale = 1; if (argc >= 3) @@ -457,6 +60,8 @@ int main(int argc, char * argv[]) if(scale < 1 || scale > 10) scale = 1; + explicitSingletons->engine = std::make_unique(); + SDLOpen(); StopTextInput(); @@ -468,38 +73,21 @@ int main(int argc, char * argv[]) ui::Engine::Ref().SetAltFullscreen(altFullscreen); ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling); - engine = &ui::Engine::Ref(); - engine->SetMaxSize(desktopWidth, desktopHeight); - engine->Begin(WINDOWW, WINDOWH); - engine->SetFastQuit(true); - - GameController * gameController = NULL; + auto &engine = ui::Engine::Ref(); + engine.Begin(); + engine.SetFastQuit(true); if (argc >= 2) { - engine->ShowWindow(new FontEditor(argv[1])); + engine.ShowWindow(new FontEditor(argv[1])); } else { std::cerr << "path to font.cpp not supplied" << std::endl; - exit(1); + Platform::Exit(1); } EngineProcess(); - ui::Engine::Ref().CloseWindow(); - delete gameController; - delete ui::Engine::Ref().g; - if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL) - { - // * nvidia-460 egl registers callbacks with x11 that end up being called - // after egl is unloaded unless we grab it here and release it after - // sdl closes the display. this is an nvidia driver weirdness but - // technically an sdl bug. glfw has this fixed: - // https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081 - SDL_GL_LoadLibrary(NULL); - SDL_QuitSubSystem(SDL_INIT_VIDEO); - SDL_GL_UnloadLibrary(); - } - SDL_Quit(); + Platform::Exit(0); return 0; } diff --git a/src/PowderToyRenderer.cpp b/src/PowderToyRenderer.cpp index 68c4d03f5..78a3d91f1 100644 --- a/src/PowderToyRenderer.cpp +++ b/src/PowderToyRenderer.cpp @@ -1,42 +1,17 @@ -#include "Config.h" #include "graphics/Graphics.h" #include "graphics/Renderer.h" - +#include "common/String.h" +#include "common/tpt-rand.h" +#include "Format.h" +#include "gui/interface/Engine.h" +#include "client/GameSave.h" +#include "simulation/Simulation.h" +#include "common/platform/Platform.h" #include #include #include #include -#include "common/String.h" -#include "Format.h" -#include "gui/interface/Engine.h" - -#include "client/GameSave.h" -#include "simulation/Simulation.h" - - -void EngineProcess() {} -void ClipboardPush(ByteString) {} -ByteString ClipboardPull() { return ""; } -int GetModifiers() { return 0; } -void SetCursorEnabled(int enabled) {} -unsigned int GetTicks() { return 0; } - -static bool ReadFile(std::vector &fileData, ByteString filename) -{ - std::ifstream f(filename, std::ios::binary); - 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) - { - std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl; - return false; - } - return true; -} - int main(int argc, char *argv[]) { if (!argv[1] || !argv[2]) { @@ -47,15 +22,15 @@ int main(int argc, char *argv[]) auto outputFilename = ByteString(argv[2]) + ".png"; std::vector fileData; - if (!ReadFile(fileData, inputFilename)) + if (!Platform::ReadFile(fileData, inputFilename)) { return 1; } - GameSave * gameSave = NULL; + std::unique_ptr gameSave; try { - gameSave = new GameSave(fileData); + gameSave = std::make_unique(fileData, false); } catch (ParseException &e) { @@ -65,11 +40,11 @@ int main(int argc, char *argv[]) } Simulation * sim = new Simulation(); - Renderer * ren = new Renderer(new Graphics(), sim); + Renderer * ren = new Renderer(sim); if (gameSave) { - sim->Load(gameSave, true); + sim->Load(gameSave.get(), true, { 0, 0 }); //Render save ren->decorations_enable = true; @@ -81,19 +56,19 @@ int main(int argc, char *argv[]) frame--; ren->render_parts(); ren->render_fire(); - ren->clearScreen(1.0f); + ren->clearScreen(); } } else { - int w = Graphics::textwidth("Save file invalid")+16, x = (XRES-w)/2, y = (YRES-24)/2; - ren->drawrect(x, y, w, 24, 192, 192, 192, 255); - ren->drawtext(x+8, y+8, "Save file invalid", 192, 192, 240, 255); + 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)); } ren->RenderBegin(); ren->RenderEnd(); - VideoBuffer screenBuffer = ren->DumpFrame(); - screenBuffer.WritePNG(outputFilename); + if (auto data = ren->DumpFrame().ToPNG()) + Platform::WriteFile(*data, outputFilename); } diff --git a/src/PowderToySDL.cpp b/src/PowderToySDL.cpp index b416fb6e2..4965d4778 100644 --- a/src/PowderToySDL.cpp +++ b/src/PowderToySDL.cpp @@ -1,66 +1,17 @@ +#include "PowderToySDL.h" +#include "SimulationConfig.h" +#include "WindowIcon.h" #include "Config.h" -#include "common/tpt-minmax.h" - -#include -#include -#include -#include -#ifdef WIN -#include -#endif -#include "SDLCompat.h" - -#ifdef X86_SSE -#include -#endif -#ifdef X86_SSE3 -#include -#endif - -#include -#if defined(LIN) -# include "icon_exe.png.h" -#endif -#include -#include - -#ifndef WIN -# include -#endif -#ifdef MACOSX -# ifdef DEBUG -# undef DEBUG -# define DEBUG 1 -# endif -# include -#endif -#include - -#include "Format.h" -#include "Misc.h" - -#include "client/Client.h" -#include "client/GameSave.h" -#include "client/SaveFile.h" -#include "client/SaveInfo.h" -#include "common/Platform.h" -#include "graphics/Graphics.h" -#include "gui/Style.h" - -#include "gui/game/GameController.h" -#include "gui/game/GameView.h" -#include "gui/dialogues/ConfirmPrompt.h" -#include "gui/dialogues/ErrorMessage.h" #include "gui/interface/Engine.h" -#include "gui/interface/Keys.h" +#include "graphics/Graphics.h" +#include "common/platform/Platform.h" +#include -#include "SDLCompat.h" - -int desktopWidth = 1280, desktopHeight = 1024; - -SDL_Window * sdl_window; -SDL_Renderer * sdl_renderer; -SDL_Texture * sdl_texture; +int desktopWidth = 1280; +int desktopHeight = 1024; +SDL_Window *sdl_window = NULL; +SDL_Renderer *sdl_renderer = NULL; +SDL_Texture *sdl_texture = NULL; int scale = 1; bool fullscreen = false; bool altFullscreen = false; @@ -68,6 +19,15 @@ bool forceIntegerScaling = true; bool resizable = false; bool momentumScroll = true; bool showAvatars = true; +uint64_t lastTick = 0; +uint64_t lastFpsUpdate = 0; +bool showLargeScreenDialog = false; +int mousex = 0; +int mousey = 0; +int mouseButton = 0; +bool mouseDown = false; +bool calculatedInitialMouse = false; +bool hasMouseMoved = false; void StartTextInput() { @@ -104,46 +64,9 @@ int GetModifiers() return SDL_GetModState(); } -void LoadWindowPosition() +unsigned int GetTicks() { - int savedWindowX = Client::Ref().GetPrefInteger("WindowX", INT_MAX); - int savedWindowY = Client::Ref().GetPrefInteger("WindowY", INT_MAX); - - int borderTop, borderLeft; - SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr); - // Sometimes (Windows), the border size may not be reported for 200+ frames - // So just have a default of 5 to ensure the window doesn't get stuck where it can't be moved - if (borderTop == 0) - borderTop = 5; - - int numDisplays = SDL_GetNumVideoDisplays(); - SDL_Rect displayBounds; - bool ok = false; - for (int i = 0; i < numDisplays; i++) - { - SDL_GetDisplayBounds(i, &displayBounds); - if (savedWindowX + borderTop > displayBounds.x && savedWindowY + borderLeft > displayBounds.y && - savedWindowX + borderTop < displayBounds.x + displayBounds.w && - savedWindowY + borderLeft < displayBounds.y + displayBounds.h) - { - ok = true; - break; - } - } - if (ok) - SDL_SetWindowPosition(sdl_window, savedWindowX + borderLeft, savedWindowY + borderTop); -} - -void SaveWindowPosition() -{ - int x, y; - SDL_GetWindowPosition(sdl_window, &x, &y); - - int borderTop, borderLeft; - SDL_GetWindowBordersSize(sdl_window, &borderTop, &borderLeft, nullptr, nullptr); - - Client::Ref().SetPref("WindowX", x - borderLeft); - Client::Ref().SetPref("WindowY", y - borderTop); + return SDL_GetTicks(); } void CalculateMousePosition(int *x, int *y) @@ -159,7 +82,7 @@ void CalculateMousePosition(int *x, int *y) *y = (globalMy - windowY) / scale; } -void blit(pixel * vid) +void blit(pixel *vid) { SDL_UpdateTexture(sdl_texture, NULL, vid, WINDOWW * sizeof (Uint32)); // need to clear the renderer if there are black edges (fullscreen, or resizable window) @@ -169,19 +92,18 @@ void blit(pixel * vid) SDL_RenderPresent(sdl_renderer); } -bool RecreateWindow(); void SDLOpen() { if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "Initializing SDL (video subsystem): %s\n", SDL_GetError()); - exit(-1); + Platform::Exit(-1); } if (!RecreateWindow()) { fprintf(stderr, "Creating SDL window: %s\n", SDL_GetError()); - exit(-1); + Platform::Exit(-1); } int displayIndex = SDL_GetWindowDisplayIndex(sdl_window); @@ -195,16 +117,26 @@ void SDLOpen() } } -#ifdef LIN - std::vector imageData; - int imgw, imgh; - if (PngDataToPixels(imageData, imgw, imgh, reinterpret_cast(icon_exe_png), icon_exe_png_size, false)) + if constexpr (SET_WINDOW_ICON) { - SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(&imageData[0], imgw, imgh, 32, imgw * sizeof(pixel), 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); - SDL_SetWindowIcon(sdl_window, icon); - SDL_FreeSurface(icon); + WindowIcon(sdl_window); } -#endif +} + +void SDLClose() +{ + if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL) + { + // * nvidia-460 egl registers callbacks with x11 that end up being called + // after egl is unloaded unless we grab it here and release it after + // sdl closes the display. this is an nvidia driver weirdness but + // technically an sdl bug. glfw has this fixed: + // https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081 + SDL_GL_LoadLibrary(NULL); + SDL_QuitSubSystem(SDL_INIT_VIDEO); + SDL_GL_UnloadLibrary(); + } + SDL_Quit(); } void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_) @@ -286,39 +218,19 @@ bool RecreateWindow() //SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); //SDL_SetWindowResizable(sdl_window, SDL_TRUE); - if (!Client::Ref().IsFirstRun()) - LoadWindowPosition(); + LoadWindowPosition(); return true; } -unsigned int GetTicks() -{ - return SDL_GetTicks(); -} - -int elapsedTime = 0, currentTime = 0, lastTime = 0, currentFrame = 0; -unsigned int lastTick = 0; -unsigned int lastFpsUpdate = 0; -float fps = 0; -ui::Engine * engine = NULL; -bool showLargeScreenDialog = false; -float currentWidth, currentHeight; - -int mousex = 0, mousey = 0; -int mouseButton = 0; -bool mouseDown = false; - -bool calculatedInitialMouse = false, delay = false; -bool hasMouseMoved = false; - -void EventProcess(SDL_Event event) +void EventProcess(const SDL_Event &event) { + auto &engine = ui::Engine::Ref(); switch (event.type) { case SDL_QUIT: - if (engine->GetFastQuit() || engine->CloseWindow()) - engine->Exit(); + if (engine.GetFastQuit() || engine.CloseWindow()) + engine.Exit(); break; case SDL_KEYDOWN: if (SDL_GetModState() & KMOD_GUI) @@ -326,30 +238,30 @@ void EventProcess(SDL_Event event) break; } if (!event.key.repeat && event.key.keysym.sym == 'q' && (event.key.keysym.mod&KMOD_CTRL)) - engine->ConfirmExit(); + engine.ConfirmExit(); else - engine->onKeyPress(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT); + engine.onKeyPress(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT); break; case SDL_KEYUP: if (SDL_GetModState() & KMOD_GUI) { break; } - engine->onKeyRelease(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT); + engine.onKeyRelease(event.key.keysym.sym, event.key.keysym.scancode, event.key.repeat, event.key.keysym.mod&KMOD_SHIFT, event.key.keysym.mod&KMOD_CTRL, event.key.keysym.mod&KMOD_ALT); break; case SDL_TEXTINPUT: if (SDL_GetModState() & KMOD_GUI) { break; } - engine->onTextInput(ByteString(event.text.text).FromUtf8()); + engine.onTextInput(ByteString(event.text.text).FromUtf8()); break; case SDL_TEXTEDITING: if (SDL_GetModState() & KMOD_GUI) { break; } - engine->onTextEditing(ByteString(event.edit.text).FromUtf8(), event.edit.start); + engine.onTextEditing(ByteString(event.edit.text).FromUtf8(), event.edit.start); break; case SDL_MOUSEWHEEL: { @@ -361,18 +273,18 @@ void EventProcess(SDL_Event event) y *= -1; } - engine->onMouseWheel(mousex, mousey, y); // TODO: pass x? + engine.onMouseWheel(mousex, mousey, y); // TODO: pass x? break; } case SDL_MOUSEMOTION: mousex = event.motion.x; mousey = event.motion.y; - engine->onMouseMove(mousex, mousey); + engine.onMouseMove(mousex, mousey); hasMouseMoved = true; break; case SDL_DROPFILE: - engine->onFileDrop(event.drop.file); + engine.onFileDrop(event.drop.file); SDL_free(event.drop.file); break; case SDL_MOUSEBUTTONDOWN: @@ -383,12 +295,13 @@ void EventProcess(SDL_Event event) mousey = event.button.y; } mouseButton = event.button.button; - engine->onMouseClick(mousex, mousey, mouseButton); + engine.onMouseClick(mousex, mousey, mouseButton); mouseDown = true; -#if !defined(NDEBUG) && !defined(DEBUG) - SDL_CaptureMouse(SDL_TRUE); -#endif + if constexpr (!DEBUG) + { + SDL_CaptureMouse(SDL_TRUE); + } break; case SDL_MOUSEBUTTONUP: // if mouse hasn't moved yet, sdl will send 0,0. We don't want that @@ -398,12 +311,13 @@ void EventProcess(SDL_Event event) mousey = event.button.y; } mouseButton = event.button.button; - engine->onMouseUnclick(mousex, mousey, mouseButton); + engine.onMouseUnclick(mousex, mousey, mouseButton); mouseDown = false; -#if !defined(NDEBUG) && !defined(DEBUG) - SDL_CaptureMouse(SDL_FALSE); -#endif + if constexpr (!DEBUG) + { + SDL_CaptureMouse(SDL_FALSE); + } break; case SDL_WINDOWEVENT: { @@ -414,119 +328,79 @@ void EventProcess(SDL_Event event) { //initial mouse coords, sdl won't tell us this if mouse hasn't moved CalculateMousePosition(&mousex, &mousey); - engine->initialMouse(mousex, mousey); - engine->onMouseMove(mousex, mousey); + engine.initialMouse(mousex, mousey); + engine.onMouseMove(mousex, mousey); calculatedInitialMouse = true; } break; - // This event would be needed in certain glitchy cases of window resizing - // But for all currently tested cases, it isn't needed - /*case SDL_WINDOWEVENT_RESIZED: - { - float width = event.window.data1; - float height = event.window.data2; - - currentWidth = width; - currentHeight = height; - // this "* scale" thing doesn't really work properly - // currently there is a bug where input doesn't scale properly after resizing, only when double scale mode is active - inputScaleH = (float)WINDOWW * scale / currentWidth; - inputScaleV = (float)WINDOWH * scale / currentHeight; - std::cout << "Changing input scale to " << inputScaleH << "x" << inputScaleV << std::endl; - break; - }*/ - // This would send a mouse up event when focus is lost - // Not even sdl itself will know when the mouse was released if it happens in another window - // So it will ignore the next mouse down (after tpt is re-focused) and not send any events at all - // This is more unintuitive than pretending the mouse is still down when it's not, so this code is commented out - /*case SDL_WINDOWEVENT_FOCUS_LOST: - if (mouseDown) - { - mouseDown = false; - engine->onMouseUnclick(mousex, mousey, mouseButton); - } - break;*/ } break; } } } -void LargeScreenDialog() -{ - StringBuilder message; - message << "Switching to " << scale << "x size mode since your screen was determined to be large enough: "; - message << desktopWidth << "x" << desktopHeight << " detected, " << WINDOWW*scale << "x" << WINDOWH*scale << " required"; - message << "\nTo undo this, hit Cancel. You can change this in settings at any time."; - if (!ConfirmPrompt::Blocking("Large screen detected", message.Build())) - { - Client::Ref().SetPref("Scale", 1); - engine->SetScale(1); - } -} - void EngineProcess() { - double frameTimeAvg = 0.0f, correctedFrameTimeAvg = 0.0f; + double correctedFrameTimeAvg = 0; SDL_Event event; - int drawingTimer = 0; - int frameStart = 0; + uint64_t drawingTimer = 0; + auto frameStart = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000); - while(engine->Running()) + auto &engine = ui::Engine::Ref(); + while(engine.Running()) { - int oldFrameStart = frameStart; - frameStart = SDL_GetTicks(); - drawingTimer += frameStart - oldFrameStart; - - if(engine->Broken()) { engine->UnBreak(); break; } + if(engine.Broken()) { engine.UnBreak(); break; } event.type = 0; while (SDL_PollEvent(&event)) { EventProcess(event); event.type = 0; //Clear last event } - if(engine->Broken()) { engine->UnBreak(); break; } + if(engine.Broken()) { engine.UnBreak(); break; } - engine->Tick(); + engine.Tick(); int drawcap = ui::Engine::Ref().GetDrawingFrequencyLimit(); - if (!drawcap || drawingTimer > 1000.f/drawcap) + if (!drawcap || drawingTimer > 1e9f / drawcap) { - engine->Draw(); + engine.Draw(); drawingTimer = 0; - if (scale != engine->Scale || fullscreen != engine->Fullscreen || - altFullscreen != engine->GetAltFullscreen() || - forceIntegerScaling != engine->GetForceIntegerScaling() || resizable != engine->GetResizable()) + if (scale != engine.Scale || fullscreen != engine.Fullscreen || + altFullscreen != engine.GetAltFullscreen() || + forceIntegerScaling != engine.GetForceIntegerScaling() || resizable != engine.GetResizable()) { - SDLSetScreen(engine->Scale, engine->GetResizable(), engine->Fullscreen, engine->GetAltFullscreen(), - engine->GetForceIntegerScaling()); + SDLSetScreen(engine.Scale, engine.GetResizable(), engine.Fullscreen, engine.GetAltFullscreen(), + engine.GetForceIntegerScaling()); } - blit(engine->g->vid); + blit(engine.g->Data()); } - - int frameTime = SDL_GetTicks() - frameStart; - frameTimeAvg = frameTimeAvg * 0.8 + frameTime * 0.2; - float fpsLimit = ui::Engine::Ref().FpsLimit; - if(fpsLimit > 2) + auto fpsLimit = ui::Engine::Ref().FpsLimit; + auto now = uint64_t(SDL_GetTicks()) * UINT64_C(1'000'000); + auto oldFrameStart = frameStart; + frameStart = now; + if (fpsLimit > 2) { - double offset = 1000.0 / fpsLimit - frameTimeAvg; - if(offset > 0) - SDL_Delay(Uint32(offset + 0.5)); + auto timeBlockDuration = uint64_t(UINT64_C(1'000'000'000) / fpsLimit); + auto oldFrameStartTimeBlock = oldFrameStart / timeBlockDuration; + auto frameStartTimeBlock = oldFrameStartTimeBlock + 1U; + frameStart = std::max(frameStart, frameStartTimeBlock * timeBlockDuration); + SDL_Delay((frameStart - now) / UINT64_C(1'000'000)); } - int correctedFrameTime = SDL_GetTicks() - frameStart; - correctedFrameTimeAvg = correctedFrameTimeAvg * 0.95 + correctedFrameTime * 0.05; - if (frameStart - lastFpsUpdate > 200) + auto correctedFrameTime = frameStart - oldFrameStart; + drawingTimer += correctedFrameTime; + correctedFrameTimeAvg = correctedFrameTimeAvg + (correctedFrameTime - correctedFrameTimeAvg) * 0.05; + if (frameStart - lastFpsUpdate > UINT64_C(200'000'000)) { - engine->SetFps(1000.0 / correctedFrameTimeAvg); + engine.SetFps(1e9f / correctedFrameTimeAvg); lastFpsUpdate = frameStart; } - if (frameStart - lastTick > 100) + if (frameStart - lastTick > UINT64_C(100'000'000)) { lastTick = frameStart; - Client::Ref().Tick(); + TickClient(); } if (showLargeScreenDialog) { @@ -534,438 +408,8 @@ void EngineProcess() LargeScreenDialog(); } } -#ifdef DEBUG - std::cout << "Breaking out of EngineProcess" << std::endl; -#endif -} - -void BlueScreen(String detailMessage) -{ - ui::Engine * engine = &ui::Engine::Ref(); - engine->g->fillrect(0, 0, engine->GetWidth(), engine->GetHeight(), 17, 114, 169, 210); - - String errorTitle = "ERROR"; - String errorDetails = "Details: " + detailMessage; - String errorHelp = "An unrecoverable fault has occurred, please report the error by visiting the website below\n" - SCHEME SERVER; - int currentY = 0, width, height; - int errorWidth = 0; - Graphics::textsize(errorHelp, errorWidth, height); - - engine->g->drawtext((engine->GetWidth()/2)-(errorWidth/2), ((engine->GetHeight()/2)-100) + currentY, errorTitle.c_str(), 255, 255, 255, 255); - Graphics::textsize(errorTitle, width, height); - currentY += height + 4; - - engine->g->drawtext((engine->GetWidth()/2)-(errorWidth/2), ((engine->GetHeight()/2)-100) + currentY, errorDetails.c_str(), 255, 255, 255, 255); - Graphics::textsize(errorTitle, width, height); - currentY += height + 4; - - engine->g->drawtext((engine->GetWidth()/2)-(errorWidth/2), ((engine->GetHeight()/2)-100) + currentY, errorHelp.c_str(), 255, 255, 255, 255); - Graphics::textsize(errorTitle, width, height); - currentY += height + 4; - - //Death loop - SDL_Event event; - while(true) + if constexpr (DEBUG) { - while (SDL_PollEvent(&event)) - if(event.type == SDL_QUIT) - exit(-1); - blit(engine->g->vid); + std::cout << "Breaking out of EngineProcess" << std::endl; } } - -void SigHandler(int signal) -{ - switch(signal){ - case SIGSEGV: - BlueScreen("Memory read/write error"); - break; - case SIGFPE: - BlueScreen("Floating point exception"); - break; - case SIGILL: - BlueScreen("Program execution exception"); - break; - case SIGABRT: - BlueScreen("Unexpected program abort"); - break; - } -} - -constexpr int SCALE_MAXIMUM = 10; -constexpr int SCALE_MARGIN = 30; - -int GuessBestScale() -{ - const int widthNoMargin = desktopWidth - SCALE_MARGIN; - const int widthGuess = widthNoMargin / WINDOWW; - - const int heightNoMargin = desktopHeight - SCALE_MARGIN; - const int heightGuess = heightNoMargin / WINDOWH; - - int guess = std::min(widthGuess, heightGuess); - if(guess < 1 || guess > SCALE_MAXIMUM) - guess = 1; - - return guess; -} - -int main(int argc, char * argv[]) -{ -#if defined(DEBUG) && defined(_MSC_VER) - _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); -#endif - currentWidth = WINDOWW; - currentHeight = WINDOWH; - - - // https://bugzilla.libsdl.org/show_bug.cgi?id=3796 - if (SDL_Init(0) < 0) - { - fprintf(stderr, "Initializing SDL: %s\n", SDL_GetError()); - return 1; - } - - Platform::originalCwd = Platform::GetCwd(); - - using Argument = std::optional; - std::map arguments; - - for (auto i = 1; i < argc; ++i) - { - auto str = ByteString(argv[i]); - if (str.BeginsWith("file://")) - { - arguments.insert({ "open", format::URLDecode(str.substr(7 /* length of the "file://" prefix */)) }); - } - else if (str.BeginsWith("ptsave:")) - { - arguments.insert({ "ptsave", str }); - } - else if (auto split = str.SplitBy(':')) - { - arguments.insert({ split.Before(), split.After() }); - } - else if (auto split = str.SplitBy('=')) - { - arguments.insert({ split.Before(), split.After() }); - } - else if (str == "open" || str == "ptsave" || str == "ddir") - { - if (i + 1 < argc) - { - arguments.insert({ str, argv[i + 1] }); - i += 1; - } - else - { - std::cerr << "no value provided for command line parameter " << str << std::endl; - } - } - else - { - arguments.insert({ str, "" }); // so .has_value() is true - } - } - - auto ddirArg = arguments["ddir"]; - if (ddirArg.has_value()) - { -#ifdef WIN - int failure = _chdir(ddirArg.value().c_str()); -#else - int failure = chdir(ddirArg.value().c_str()); -#endif - if (!failure) - Platform::sharedCwd = Platform::GetCwd(); - else - perror("failed to chdir to requested ddir"); - } - else - { - char *ddir = SDL_GetPrefPath(NULL, APPDATA); -#ifdef WIN - struct _stat s; - if (_stat("powder.pref", &s) != 0) -#else - struct stat s; - if (stat("powder.pref", &s) != 0) -#endif - { - if (ddir) - { -#ifdef WIN - int failure = _chdir(ddir); -#else - int failure = chdir(ddir); -#endif - if (failure) - { - perror("failed to chdir to default ddir"); - SDL_free(ddir); - ddir = nullptr; - } - } - } - - if (ddir) - { - Platform::sharedCwd = ddir; - SDL_free(ddir); - } - } - - scale = Client::Ref().GetPrefInteger("Scale", 1); - resizable = Client::Ref().GetPrefBool("Resizable", false); - fullscreen = Client::Ref().GetPrefBool("Fullscreen", false); - altFullscreen = Client::Ref().GetPrefBool("AltFullscreen", false); - forceIntegerScaling = Client::Ref().GetPrefBool("ForceIntegerScaling", true); - momentumScroll = Client::Ref().GetPrefBool("MomentumScroll", true); - showAvatars = Client::Ref().GetPrefBool("ShowAvatars", true); - - auto true_string = [](ByteString str) { - str = str.ToLower(); - return str == "true" || - str == "t" || - str == "on" || - str == "yes" || - str == "y" || - str == ""; // standalone "redirect" or "disable-bluescreen" or similar arguments - }; - auto true_arg = [&true_string](Argument arg) { - return arg.has_value() && true_string(arg.value()); - }; - - auto kioskArg = arguments["kiosk"]; - if (kioskArg.has_value()) - { - fullscreen = true_string(kioskArg.value()); - Client::Ref().SetPref("Fullscreen", fullscreen); - } - - if (true_arg(arguments["redirect"])) - { - FILE *new_stdout = freopen("stdout.log", "w", stdout); - FILE *new_stderr = freopen("stderr.log", "w", stderr); - if (!new_stdout || !new_stderr) - { - exit(42); - } - } - - auto scaleArg = arguments["scale"]; - if (scaleArg.has_value()) - { - try - { - scale = scaleArg.value().ToNumber(); - Client::Ref().SetPref("Scale", scale); - } - catch (const std::runtime_error &e) - { - std::cerr << "failed to set scale: " << e.what() << std::endl; - } - } - - auto clientConfig = [](Argument arg, ByteString name, ByteString defaultValue) { - ByteString value; - if (arg.has_value()) - { - if (value == "") - { - value = defaultValue; - } - Client::Ref().SetPref(name, value); - } - else - { - value = Client::Ref().GetPrefByteString(name, defaultValue); - } - return value; - }; - ByteString proxyString = clientConfig(arguments["proxy"], "Proxy", ""); - ByteString cafileString = clientConfig(arguments["cafile"], "CAFile", ""); - ByteString capathString = clientConfig(arguments["capath"], "CAPath", ""); - - bool disableNetwork = true_arg(arguments["disable-network"]); - - Client::Ref().Initialise(proxyString, cafileString, capathString, disableNetwork); - - // TODO: maybe bind the maximum allowed scale to screen size somehow - if(scale < 1 || scale > SCALE_MAXIMUM) - scale = 1; - - SDLOpen(); - - if (Client::Ref().IsFirstRun()) - { - scale = GuessBestScale(); - if (scale > 1) - { - Client::Ref().SetPref("Scale", scale); - SDL_SetWindowSize(sdl_window, WINDOWW * scale, WINDOWH * scale); - showLargeScreenDialog = true; - } - } - - StopTextInput(); - - ui::Engine::Ref().g = new Graphics(); - ui::Engine::Ref().Scale = scale; - ui::Engine::Ref().SetResizable(resizable); - ui::Engine::Ref().Fullscreen = fullscreen; - ui::Engine::Ref().SetAltFullscreen(altFullscreen); - ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling); - ui::Engine::Ref().MomentumScroll = momentumScroll; - ui::Engine::Ref().ShowAvatars = showAvatars; - - engine = &ui::Engine::Ref(); - engine->SetMaxSize(desktopWidth, desktopHeight); - engine->Begin(WINDOWW, WINDOWH); - engine->SetFastQuit(Client::Ref().GetPrefBool("FastQuit", true)); - -#if !defined(DEBUG) - bool enableBluescreen = !true_arg(arguments["disable-bluescreen"]); - if (enableBluescreen) - { - //Get ready to catch any dodgy errors - signal(SIGSEGV, SigHandler); - signal(SIGFPE, SigHandler); - signal(SIGILL, SigHandler); - signal(SIGABRT, SigHandler); - } -#endif - -#ifdef X86_SSE - _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); -#endif -#ifdef X86_SSE3 - _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); -#endif - - GameController * gameController = NULL; - - auto wrapWithBluescreen = [&]() { - gameController = new GameController(); - engine->ShowWindow(gameController->GetView()); - - auto openArg = arguments["open"]; - if (openArg.has_value()) - { -#ifdef DEBUG - std::cout << "Loading " << openArg.value() << std::endl; -#endif - if (Platform::FileExists(openArg.value())) - { - try - { - std::vector gameSaveData; - if (!Platform::ReadFile(gameSaveData, openArg.value())) - { - new ErrorMessage("Error", "Could not read file"); - } - else - { - SaveFile * newFile = new SaveFile(openArg.value()); - GameSave * newSave = new GameSave(std::move(gameSaveData)); - newFile->SetGameSave(newSave); - gameController->LoadSaveFile(newFile); - delete newFile; - } - - } - catch (std::exception & e) - { - new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ; - } - } - else - { - new ErrorMessage("Error", "Could not open file"); - } - } - - auto ptsaveArg = arguments["ptsave"]; - if (ptsaveArg.has_value()) - { - engine->g->fillrect((engine->GetWidth()/2)-101, (engine->GetHeight()/2)-26, 202, 52, 0, 0, 0, 210); - engine->g->drawrect((engine->GetWidth()/2)-100, (engine->GetHeight()/2)-25, 200, 50, 255, 255, 255, 180); - engine->g->drawtext((engine->GetWidth()/2)-(Graphics::textwidth("Loading save...")/2), (engine->GetHeight()/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255); - - blit(engine->g->vid); - try - { - ByteString saveIdPart; - if (ByteString::Split split = ptsaveArg.value().SplitBy(':')) - { - if (split.Before() != "ptsave") - throw std::runtime_error("Not a ptsave link"); - saveIdPart = split.After().SplitBy('#').Before(); - } - else - throw std::runtime_error("Invalid save link"); - - if (!saveIdPart.size()) - throw std::runtime_error("No Save ID"); -#ifdef DEBUG - std::cout << "Got Ptsave: id: " << saveIdPart << std::endl; -#endif - int saveId = saveIdPart.ToNumber(); - - SaveInfo * newSave = Client::Ref().GetSave(saveId, 0); - if (!newSave) - throw std::runtime_error("Could not load save info"); - auto saveData = Client::Ref().GetSaveData(saveId, 0); - if (!saveData.size()) - throw std::runtime_error(("Could not load save\n" + Client::Ref().GetLastError()).ToUtf8()); - GameSave * newGameSave = new GameSave(std::move(saveData)); - newSave->SetGameSave(newGameSave); - - gameController->LoadSave(newSave); - delete newSave; - } - catch (std::exception & e) - { - new ErrorMessage("Error", ByteString(e.what()).FromUtf8()); - } - } - - EngineProcess(); - SaveWindowPosition(); - }; - -#if !defined(DEBUG) - if (enableBluescreen) - { - try - { - wrapWithBluescreen(); - } - catch (const std::exception &e) - { - BlueScreen(ByteString(e.what()).FromUtf8()); - } - } - else -#endif - wrapWithBluescreen(); // the else branch of the if in the #if !defined(DEBUG) - - ui::Engine::Ref().CloseWindow(); - delete gameController; - delete ui::Engine::Ref().g; - Client::Ref().Shutdown(); - if (SDL_GetWindowFlags(sdl_window) & SDL_WINDOW_OPENGL) - { - // * nvidia-460 egl registers callbacks with x11 that end up being called - // after egl is unloaded unless we grab it here and release it after - // sdl closes the display. this is an nvidia driver weirdness but - // technically an sdl bug. glfw has this fixed: - // https://github.com/glfw/glfw/commit/9e6c0c747be838d1f3dc38c2924a47a42416c081 - SDL_GL_LoadLibrary(NULL); - SDL_QuitSubSystem(SDL_INIT_VIDEO); - SDL_GL_UnloadLibrary(); - } - SDL_Quit(); - return 0; -} diff --git a/src/PowderToySDL.h b/src/PowderToySDL.h new file mode 100644 index 000000000..3c7b66508 --- /dev/null +++ b/src/PowderToySDL.h @@ -0,0 +1,47 @@ +#pragma once +#include "common/String.h" +#include "graphics/Pixel.h" +#include +#include + +extern int desktopWidth; +extern int desktopHeight; +extern SDL_Window *sdl_window; +extern SDL_Renderer *sdl_renderer; +extern SDL_Texture *sdl_texture; +extern int scale; +extern bool fullscreen; +extern bool altFullscreen; +extern bool forceIntegerScaling; +extern bool resizable; +extern bool momentumScroll; +extern bool showAvatars; +extern uint64_t lastTick; +extern uint64_t lastFpsUpdate; +extern bool showLargeScreenDialog; +extern int mousex; +extern int mousey; +extern int mouseButton; +extern bool mouseDown; +extern bool calculatedInitialMouse; +extern bool hasMouseMoved; + +void EngineProcess(); +void StartTextInput(); +void StopTextInput(); +void SetTextInputRect(int x, int y, int w, int h); +void ClipboardPush(ByteString text); +ByteString ClipboardPull(); +int GetModifiers(); +unsigned int GetTicks(); +void CalculateMousePosition(int *x, int *y); +void blit(pixel *vid); +void SDLOpen(); +void SDLClose(); +void SDLSetScreen(int scale_, bool resizable_, bool fullscreen_, bool altFullscreen_, bool forceIntegerScaling_); +bool RecreateWindow(); +void LoadWindowPosition(); +void SaveWindowPosition(); +void LargeScreenDialog(); +void TickClient(); +void EventProcess(const SDL_Event &event); diff --git a/src/Probability.cpp b/src/Probability.cpp index 9df04015f..a4f1eea05 100644 --- a/src/Probability.cpp +++ b/src/Probability.cpp @@ -14,10 +14,9 @@ */ #include "Probability.h" - +#include "common/tpt-rand.h" #include #include -#include "common/tpt-rand.h" namespace Probability { diff --git a/src/Probability.h b/src/Probability.h index 32b76e47a..3c62bbf2b 100644 --- a/src/Probability.h +++ b/src/Probability.h @@ -13,12 +13,8 @@ * along with this program. If not, see . */ -#ifndef tptmath_h -#define tptmath_h -#include "Config.h" - +#pragma once // This file is used for EMP, to simulate many EMP going off at once at the end of the frame - #include namespace Probability @@ -41,5 +37,3 @@ namespace Probability unsigned int calc(float randFloat); }; } - -#endif diff --git a/src/SDLCompat.h b/src/SDLCompat.h deleted file mode 100644 index 26b093ab6..000000000 --- a/src/SDLCompat.h +++ /dev/null @@ -1,7 +0,0 @@ -#include "Config.h" -#include -#ifdef INCLUDE_SYSWM -# if defined(WIN) -# include -# endif // WIN -#endif // INCLUDE_SYSWM diff --git a/src/SimulationConfig.h b/src/SimulationConfig.h new file mode 100644 index 000000000..bee3ff9c0 --- /dev/null +++ b/src/SimulationConfig.h @@ -0,0 +1,63 @@ +#pragma once +#include +#include + +constexpr int MENUSIZE = 40; +constexpr int BARSIZE = 17; + +constexpr float M_GRAV = 6.67300e-1f; + +//CELL, the size of the pressure, gravity, and wall maps. Larger than 1 to prevent extreme lag +constexpr int CELL = 4; +constexpr Vec2 CELLS = Vec2(153, 96); +constexpr Vec2 RES = CELLS * CELL; + +constexpr int XCELLS = CELLS.X; +constexpr int YCELLS = CELLS.Y; +constexpr int NCELL = XCELLS * YCELLS; +constexpr int XRES = RES.X; +constexpr int YRES = RES.Y; +constexpr int NPART = XRES * YRES; + +constexpr int XCNTR = XRES / 2; +constexpr int YCNTR = YRES / 2; + +constexpr Vec2 WINDOW = RES + Vec2(BARSIZE, MENUSIZE); + +constexpr int WINDOWW = WINDOW.X; +constexpr int WINDOWH = WINDOW.Y; + +constexpr int MAXSIGNS = 16; + +constexpr int ISTP = CELL / 2; +constexpr float CFDS = 4.0f / CELL; +constexpr float SIM_MAXVELOCITY = 1e4f; + +//Air constants +constexpr float AIR_TSTEPP = 0.3f; +constexpr float AIR_TSTEPV = 0.4f; +constexpr float AIR_VADV = 0.3f; +constexpr float AIR_VLOSS = 0.999f; +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; + +//Photon constants +constexpr int SURF_RANGE = 10; +constexpr int NORMAL_MIN_EST = 3; +constexpr int NORMAL_INTERP = 20; +constexpr int NORMAL_FRAC = 16; + +constexpr auto REFRACT = UINT32_C(0x80000000); + +/* heavy flint glass, for awesome refraction/dispersion + this way you can make roof prisms easily */ +constexpr float GLASS_IOR = 1.9f; +constexpr float GLASS_DISP = 0.07f; + +constexpr float R_TEMP = 22; diff --git a/src/Update.cpp b/src/Update.cpp deleted file mode 100644 index 1952377ce..000000000 --- a/src/Update.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "Update.h" - -#include -#include -#ifndef WIN -#include -#endif -#if !defined(MACOSX) && !defined(BSD) -#include -#endif -#include -#include - -#ifdef WIN -# ifndef NOMINMAX -# define NOMINMAX -# endif -# include -#else -# include -# include -#endif -#ifdef MACOSX -# include -# include -#endif - -#include "common/Platform.h" - -// returns 1 on failure, 0 on success -int update_start(char *data, unsigned int len) -{ - ByteString exeName = Platform::ExecutableName(), updName; - FILE *f; - - if (!exeName.length()) - return 1; - -#ifdef WIN - updName = exeName; - ByteString extension = exeName.substr(exeName.length() - 4); - if (extension == ".exe") - updName = exeName.substr(0, exeName.length() - 4); - updName = updName + "_upd.exe"; - - if (!MoveFile(Platform::WinWiden(exeName).c_str(), Platform::WinWiden(updName).c_str())) - return 1; - - f = fopen(exeName.c_str(), "wb"); - if (!f) - return 1; - if (fwrite(data, 1, len, f) != len) - { - fclose(f); - Platform::RemoveFile(exeName); - return 1; - } - fclose(f); - - if ((uintptr_t)ShellExecute(NULL, L"open", Platform::WinWiden(exeName).c_str(), NULL, NULL, SW_SHOWNORMAL) <= 32) - { - Platform::RemoveFile(exeName); - return 1; - } - - return 0; -#else - updName = exeName + "-update"; - - f = fopen(updName.c_str(), "w"); - if (!f) - return 1; - if (fwrite(data, 1, len, f) != len) - { - fclose(f); - unlink(updName.c_str()); - return 1; - } - fclose(f); - - if (chmod(updName.c_str(), 0755)) - { - unlink(updName.c_str()); - return 1; - } - - if (rename(updName.c_str(), exeName.c_str())) - { - unlink(updName.c_str()); - return 1; - } - - execl(exeName.c_str(), "powder-update", NULL); - return 0; -#endif -} - -// returns 1 on failure, 0 on success -int update_finish() -{ -#ifdef WIN - ByteString exeName = Platform::ExecutableName(), updName; - int timeout = 5, err; - -#ifdef DEBUG - printf("Update: Current EXE name: %s\n", exeName.c_str()); -#endif - - updName = exeName; - ByteString extension = exeName.substr(exeName.length() - 4); - if (extension == ".exe") - updName = exeName.substr(0, exeName.length() - 4); - updName = updName + "_upd.exe"; - -#ifdef DEBUG - printf("Update: Temp EXE name: %s\n", updName.c_str()); -#endif - - while (!Platform::RemoveFile(updName)) - { - err = GetLastError(); - if (err == ERROR_FILE_NOT_FOUND) - { -#ifdef DEBUG - printf("Update: Temp file not deleted\n"); -#endif - // Old versions of powder toy name their update files with _update.exe, delete that upgrade file here - updName = exeName; - ByteString extension = exeName.substr(exeName.length() - 4); - if (extension == ".exe") - updName = exeName.substr(0, exeName.length() - 4); - updName = updName + "_update.exe"; - Platform::RemoveFile(updName); - return 0; - } - Sleep(500); - timeout--; - if (timeout <= 0) - { -#ifdef DEBUG - printf("Update: Delete timeout\n"); -#endif - return 1; - } - } -#endif - return 0; -} - -void update_cleanup() -{ -#ifdef WIN - update_finish(); -#endif -} diff --git a/src/Update.h b/src/Update.h deleted file mode 100644 index f10fe57da..000000000 --- a/src/Update.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef UPDATE_H_ -#define UPDATE_H_ -#include "Config.h" - -//char *exe_name(void); -int update_start(char *data, unsigned int len); -int update_finish(); -void update_cleanup(); - -#endif /* UPDATE_H_ */ diff --git a/src/WindowIcon.cpp b/src/WindowIcon.cpp new file mode 100644 index 000000000..37602f205 --- /dev/null +++ b/src/WindowIcon.cpp @@ -0,0 +1,15 @@ +#include "Format.h" +#include "graphics/Graphics.h" +#include "WindowIcon.h" + +#include "icon_exe.png.h" + +void WindowIcon(SDL_Window *window) +{ + if (auto image = format::PixelsFromPNG(std::vector(icon_exe_png, icon_exe_png + icon_exe_png_size))) + { + SDL_Surface *icon = SDL_CreateRGBSurfaceFrom(image->data(), image->Size().X, image->Size().Y, 32, image->Size().Y * sizeof(pixel), 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000); + SDL_SetWindowIcon(window, icon); + SDL_FreeSurface(icon); + } +} diff --git a/src/WindowIcon.h b/src/WindowIcon.h new file mode 100644 index 000000000..0ffce34ad --- /dev/null +++ b/src/WindowIcon.h @@ -0,0 +1,4 @@ +#pragma once +#include + +void WindowIcon(SDL_Window *window); diff --git a/src/X86KillDenormals.cpp b/src/X86KillDenormals.cpp new file mode 100644 index 000000000..0495a8e8a --- /dev/null +++ b/src/X86KillDenormals.cpp @@ -0,0 +1,9 @@ +#include "X86KillDenormals.h" +#include +#include + +void X86KillDenormals() +{ + _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON); + _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON); +} diff --git a/src/X86KillDenormals.h b/src/X86KillDenormals.h new file mode 100644 index 000000000..13ecb45ef --- /dev/null +++ b/src/X86KillDenormals.h @@ -0,0 +1,3 @@ +#pragma once + +void X86KillDenormals(); diff --git a/src/bson/BSON.cpp b/src/bson/BSON.cpp index d4d16b0ef..608249d17 100644 --- a/src/bson/BSON.cpp +++ b/src/bson/BSON.cpp @@ -975,7 +975,7 @@ void bson_fatal_msg( int ok , const char *msg ) { } bson_errprintf( "error: %s\n" , msg ); - exit( -5 ); + abort(); } diff --git a/src/bson/BSON.h b/src/bson/BSON.h index bad754be9..bb03cf7d2 100644 --- a/src/bson/BSON.h +++ b/src/bson/BSON.h @@ -18,22 +18,18 @@ * limitations under the License. */ -#ifndef _BSON_H_ -#define _BSON_H_ -#include "Config.h" - +#pragma once +#include "common/tpt-inline.h" #include #include #include #include #include #include -#include "common/tpt-inline.h" - #include -#define BSON_OK 0 -#define BSON_ERROR -1 +constexpr int BSON_OK = 0; +constexpr int BSON_ERROR = -1; static const char bson_numstrs[1000][4] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", @@ -1210,5 +1206,3 @@ static TPT_INLINE void bson_swap_endian32( void *outp, const void *inp ) { out[2] = in[1]; out[3] = in[0]; } - -#endif diff --git a/src/bzip2/bz2wrap.cpp b/src/bzip2/bz2wrap.cpp index 0d0adb991..8bf119f29 100644 --- a/src/bzip2/bz2wrap.cpp +++ b/src/bzip2/bz2wrap.cpp @@ -1,7 +1,5 @@ #include "bz2wrap.h" - #include "bzlib.h" - #include #include #include diff --git a/src/bzip2/bz2wrap.h b/src/bzip2/bz2wrap.h index 42523b03b..3404c0a31 100644 --- a/src/bzip2/bz2wrap.h +++ b/src/bzip2/bz2wrap.h @@ -1,5 +1,4 @@ #pragma once - #include #include diff --git a/src/client/Client.cpp b/src/client/Client.cpp index 1533f2e35..29ae55f80 100644 --- a/src/client/Client.cpp +++ b/src/client/Client.cpp @@ -1,157 +1,90 @@ #include "Client.h" - -#include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows - +#include "prefs/GlobalPrefs.h" +#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" +#include "client/UserInfo.h" +#include "common/platform/Platform.h" +#include "common/String.h" +#include "graphics/Graphics.h" +#include "prefs/Prefs.h" +#include "lua/CommandInterface.h" +#include "Config.h" #include #include #include #include #include #include -#include #include #include - -#ifdef MACOSX -# include "common/macosx.h" -#endif - -#ifdef LIN -# include "icon_cps.png.h" -# include "icon_exe.png.h" -# include "save.xml.h" -# include "powder.desktop.h" -#endif - -#ifdef WIN -# ifndef NOMINMAX -# define NOMINMAX -# endif -# include -# include -# include -# include -# include -# include "resource.h" -#else -# include -# include -#endif - -#include "ClientListener.h" -#include "Config.h" -#include "Format.h" -#include "MD5.h" -#include "Update.h" - -#include "client/GameSave.h" -#include "client/SaveFile.h" -#include "client/SaveInfo.h" -#include "client/UserInfo.h" -#include "common/Platform.h" -#include "common/String.h" -#include "graphics/Graphics.h" - -#ifdef LUACONSOLE -# include "lua/LuaScriptInterface.h" -#endif - -#include "client/http/RequestManager.h" -#include "gui/preview/Comment.h" +#include +#include +#include Client::Client(): messageOfTheDay("Fetching the message of the day..."), - versionCheckRequest(nullptr), - alternateVersionCheckRequest(nullptr), usingAltUpdateServer(false), updateAvailable(false), authUser(0, "") { - //Read config - std::ifstream configFile; - configFile.open("powder.pref", std::ios::binary); - if (configFile) - { - try - { - preferences.clear(); - configFile >> preferences; - int ID = preferences["User"]["ID"].asInt(); - ByteString Username = preferences["User"]["Username"].asString(); - ByteString SessionID = preferences["User"]["SessionID"].asString(); - ByteString SessionKey = preferences["User"]["SessionKey"].asString(); - ByteString Elevation = preferences["User"]["Elevation"].asString(); - - authUser.UserID = ID; - authUser.Username = Username; - authUser.SessionID = SessionID; - authUser.SessionKey = SessionKey; - if (Elevation == "Admin") - authUser.UserElevation = User::ElevationAdmin; - else if (Elevation == "Mod") - authUser.UserElevation = User::ElevationModerator; - else - authUser.UserElevation = User::ElevationNone; - } - catch (std::exception &e) - { - - } - configFile.close(); - firstRun = false; - } - else - firstRun = true; + auto &prefs = GlobalPrefs::Ref(); + authUser.UserID = prefs.Get("User.ID", 0); + authUser.Username = prefs.Get("User.Username", ByteString("")); + authUser.SessionID = prefs.Get("User.SessionID", ByteString("")); + authUser.SessionKey = prefs.Get("User.SessionKey", ByteString("")); + authUser.UserElevation = prefs.Get("User.Elevation", User::ElevationNone); + firstRun = !prefs.BackedByFile(); } -void Client::Initialise(ByteString proxy, ByteString cafile, ByteString capath, bool disableNetwork) +void Client::MigrateStampsDef() { -#if !defined(FONTEDITOR) && !defined(RENDERER) - if (GetPrefBool("version.update", false)) + std::vector data; + if (!Platform::ReadFile(data, ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, "stamps.def"))) { - SetPref("version.update", false); - update_finish(); + return; } -#endif - -#ifndef NOHTTP - if (!disableNetwork) - http::RequestManager::Ref().Initialise(proxy, cafile, capath); -#endif - - //Read stamps library - std::ifstream stampsLib; - stampsLib.open(STAMPS_DIR PATH_SEP "stamps.def", std::ios::binary); - while (!stampsLib.eof()) + for (auto i = 0; i < int(data.size()); i += 10) { - char data[11]; - memset(data, 0, 11); - stampsLib.read(data, 10); - if(!data[0]) - break; - stampIDs.push_back(data); + stampIDs.push_back(ByteString(&data[0] + i, &data[0] + i + 10)); + } +} + +void Client::Initialize() +{ + auto &prefs = GlobalPrefs::Ref(); + if (prefs.Get("version.update", false)) + { + prefs.Set("version.update", false); + Platform::UpdateFinish(); + } + + stamps = std::make_unique(ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, "stamps.json")); + stampIDs = stamps->Get("MostRecentlyUsedFirst", std::vector{}); + { + Prefs::DeferWrite dw(*stamps); + if (!stamps->BackedByFile()) + { + MigrateStampsDef(); + WriteStamps(); + } + RescanStamps(); } - stampsLib.close(); //Begin version check - versionCheckRequest = new http::Request(SCHEME SERVER "/Startup.json"); - - if (authUser.UserID) - { - versionCheckRequest->AuthHeaders(ByteString::Build(authUser.UserID), authUser.SessionID); - } + versionCheckRequest = std::make_unique(false); versionCheckRequest->Start(); - -#ifdef UPDATESERVER - // use an alternate update server - alternateVersionCheckRequest = new http::Request(SCHEME UPDATESERVER "/Startup.json"); - usingAltUpdateServer = true; - if (authUser.UserID) + if constexpr (USE_UPDATESERVER) { - alternateVersionCheckRequest->AuthHeaders(authUser.Username, ""); + // use an alternate update server + alternateVersionCheckRequest = std::make_unique(true); + alternateVersionCheckRequest->Start(); + usingAltUpdateServer = true; } - alternateVersionCheckRequest->Start(); -#endif } bool Client::IsFirstRun() @@ -170,210 +103,82 @@ String Client::GetMessageOfTheDay() return messageOfTheDay; } -void Client::AddServerNotification(std::pair notification) +void Client::AddServerNotification(ServerNotification notification) { serverNotifications.push_back(notification); notifyNewNotification(notification); } -std::vector > Client::GetServerNotifications() +std::vector Client::GetServerNotifications() { return serverNotifications; } -RequestStatus Client::ParseServerReturn(ByteString &result, int status, bool json) -{ - lastError = ""; - // no server response, return "Malformed Response" - if (status == 200 && !result.size()) - { - status = 603; - } - if (status == 302) - return RequestOkay; - if (status != 200) - { - lastError = String::Build("HTTP Error ", status, ": ", http::StatusText(status)); - return RequestFailure; - } - - if (json) - { - std::istringstream datastream(result); - Json::Value root; - - try - { - datastream >> root; - // assume everything is fine if an empty [] is returned - if (root.size() == 0) - { - return RequestOkay; - } - int status = root.get("Status", 1).asInt(); - if (status != 1) - { - lastError = ByteString(root.get("Error", "Unspecified Error").asString()).FromUtf8(); - return RequestFailure; - } - } - catch (std::exception &e) - { - // sometimes the server returns a 200 with the text "Error: 401" - if (!strncmp(result.c_str(), "Error: ", 7)) - { - status = ByteString(result.begin() + 7, result.end()).ToNumber(); - lastError = String::Build("HTTP Error ", status, ": ", http::StatusText(status)); - return RequestFailure; - } - lastError = "Could not read response: " + ByteString(e.what()).FromUtf8(); - return RequestFailure; - } - } - else - { - if (strncmp(result.c_str(), "OK", 2)) - { - lastError = result.FromUtf8(); - return RequestFailure; - } - } - return RequestOkay; -} - void Client::Tick() { - if (versionCheckRequest) + auto applyUpdateInfo = false; + if (versionCheckRequest && versionCheckRequest->CheckDone()) { - if (CheckUpdate(versionCheckRequest, true)) - versionCheckRequest = nullptr; + if (versionCheckRequest->StatusCode() == 618) + { + AddServerNotification({ "Failed to load SSL certificates", ByteString(SCHEME) + "powdertoy.co.uk/FAQ.html" }); + } + try + { + auto info = versionCheckRequest->Finish(); + if (!info.sessionGood) + { + SetAuthUser(User(0, "")); + } + if (!usingAltUpdateServer) + { + updateInfo = info.updateInfo; + applyUpdateInfo = true; + messageOfTheDay = info.messageOfTheDay; + } + for (auto ¬ification : info.notifications) + { + AddServerNotification(notification); + } + } + catch (const http::RequestError &ex) + { + if (!usingAltUpdateServer) + { + messageOfTheDay = ByteString::Build("Error while fetching MotD: ", ex.what()).FromUtf8(); + } + } + versionCheckRequest.reset(); } - if (alternateVersionCheckRequest) + if (alternateVersionCheckRequest && alternateVersionCheckRequest->CheckDone()) { - if (CheckUpdate(alternateVersionCheckRequest, false)) - alternateVersionCheckRequest = nullptr; + try + { + auto info = alternateVersionCheckRequest->Finish(); + updateInfo = info.updateInfo; + applyUpdateInfo = true; + messageOfTheDay = info.messageOfTheDay; + for (auto ¬ification : info.notifications) + { + AddServerNotification(notification); + } + } + catch (const http::RequestError &ex) + { + messageOfTheDay = ByteString::Build("Error while checking for updates: ", ex.what()).FromUtf8(); + } + alternateVersionCheckRequest.reset(); + } + if (applyUpdateInfo && !IGNORE_UPDATES) + { + if (updateInfo) + { + notifyUpdateAvailable(); + } } } -bool Client::CheckUpdate(http::Request *updateRequest, bool checkSession) -{ - //Check status on version check request - if (updateRequest->CheckDone()) - { - int status; - ByteString data = updateRequest->Finish(&status); - - if (checkSession && status == 618) - { - AddServerNotification({ "Failed to load SSL certificates", SCHEME "powdertoy.co.uk/FAQ.html" }); - } - - if (status != 200) - { - //free(data); - if (usingAltUpdateServer && !checkSession) - this->messageOfTheDay = String::Build("HTTP Error ", status, " while checking for updates: ", http::StatusText(status)); - else - this->messageOfTheDay = String::Build("HTTP Error ", status, " while fetching MotD"); - } - else if(data.size()) - { - std::istringstream dataStream(data); - - try - { - Json::Value objDocument; - dataStream >> objDocument; - - //Check session - if (checkSession) - { - if (!objDocument["Session"].asBool()) - { - SetAuthUser(User(0, "")); - } - } - - //Notifications from server - Json::Value notificationsArray = objDocument["Notifications"]; - for (Json::UInt j = 0; j < notificationsArray.size(); j++) - { - ByteString notificationLink = notificationsArray[j]["Link"].asString(); - String notificationText = ByteString(notificationsArray[j]["Text"].asString()).FromUtf8(); - - std::pair item = std::pair(notificationText, notificationLink); - AddServerNotification(item); - } - - - //MOTD - if (!usingAltUpdateServer || !checkSession) - { - this->messageOfTheDay = ByteString(objDocument["MessageOfTheDay"].asString()).FromUtf8(); - notifyMessageOfTheDay(); - -#ifndef IGNORE_UPDATES - //Check for updates - Json::Value versions = objDocument["Updates"]; -#ifndef SNAPSHOT - Json::Value stableVersion = versions["Stable"]; - int stableMajor = stableVersion["Major"].asInt(); - int stableMinor = stableVersion["Minor"].asInt(); - int stableBuild = stableVersion["Build"].asInt(); - ByteString stableFile = stableVersion["File"].asString(); - String stableChangelog = ByteString(stableVersion["Changelog"].asString()).FromUtf8(); - if (stableBuild > BUILD_NUM) - { - updateAvailable = true; - updateInfo = UpdateInfo(stableMajor, stableMinor, stableBuild, stableFile, stableChangelog, UpdateInfo::Stable); - } -#endif - - if (!updateAvailable) - { - Json::Value betaVersion = versions["Beta"]; - int betaMajor = betaVersion["Major"].asInt(); - int betaMinor = betaVersion["Minor"].asInt(); - int betaBuild = betaVersion["Build"].asInt(); - ByteString betaFile = betaVersion["File"].asString(); - String betaChangelog = ByteString(betaVersion["Changelog"].asString()).FromUtf8(); - if (betaBuild > BUILD_NUM) - { - updateAvailable = true; - updateInfo = UpdateInfo(betaMajor, betaMinor, betaBuild, betaFile, betaChangelog, UpdateInfo::Beta); - } - } - -#if defined(SNAPSHOT) || MOD_ID > 0 - Json::Value snapshotVersion = versions["Snapshot"]; - int snapshotSnapshot = snapshotVersion["Snapshot"].asInt(); - ByteString snapshotFile = snapshotVersion["File"].asString(); - String snapshotChangelog = ByteString(snapshotVersion["Changelog"].asString()).FromUtf8(); - if (snapshotSnapshot > SNAPSHOT_ID) - { - updateAvailable = true; - updateInfo = UpdateInfo(snapshotSnapshot, snapshotFile, snapshotChangelog, UpdateInfo::Snapshot); - } -#endif - - if(updateAvailable) - { - notifyUpdateAvailable(); - } -#endif - } - } - catch (std::exception & e) - { - //Do nothing - } - } - return true; - } - return false; -} - -UpdateInfo Client::GetUpdateInfo() +std::optional Client::GetUpdateInfo() { return updateInfo; } @@ -402,7 +207,7 @@ void Client::notifyAuthUserChanged() } } -void Client::notifyNewNotification(std::pair notification) +void Client::notifyNewNotification(ServerNotification notification) { for (std::vector::iterator iterator = listeners.begin(), end = listeners.end(); iterator != end; ++iterator) { @@ -427,64 +232,29 @@ void Client::RemoveListener(ClientListener * listener) } } -void Client::WritePrefs() -{ - std::ofstream configFile; - configFile.open("powder.pref", std::ios::trunc); - - if (configFile) - { - if (authUser.UserID) - { - preferences["User"]["ID"] = authUser.UserID; - preferences["User"]["SessionID"] = authUser.SessionID; - preferences["User"]["SessionKey"] = authUser.SessionKey; - preferences["User"]["Username"] = authUser.Username; - if (authUser.UserElevation == User::ElevationAdmin) - preferences["User"]["Elevation"] = "Admin"; - else if (authUser.UserElevation == User::ElevationModerator) - preferences["User"]["Elevation"] = "Mod"; - else - preferences["User"]["Elevation"] = "None"; - } - else - { - preferences["User"] = Json::nullValue; - } - configFile << preferences; - - configFile.close(); - } -} - -void Client::Shutdown() -{ - if (versionCheckRequest) - { - versionCheckRequest->Cancel(); - } - if (alternateVersionCheckRequest) - { - alternateVersionCheckRequest->Cancel(); - } - -#ifndef NOHTTP - http::RequestManager::Ref().Shutdown(); -#endif - - //Save config - WritePrefs(); -} - Client::~Client() { } - void Client::SetAuthUser(User user) { authUser = user; - WritePrefs(); + { + auto &prefs = GlobalPrefs::Ref(); + Prefs::DeferWrite dw(prefs); + if (authUser.UserID) + { + prefs.Set("User.ID", authUser.UserID); + prefs.Set("User.SessionID", authUser.SessionID); + prefs.Set("User.SessionKey", authUser.SessionKey); + prefs.Set("User.Username", authUser.Username); + prefs.Set("User.Elevation", authUser.UserElevation); + } + else + { + prefs.Clear("User"); + } + } notifyAuthUserChanged(); } @@ -493,85 +263,31 @@ User Client::GetAuthUser() return authUser; } -RequestStatus Client::UploadSave(SaveInfo & save) -{ - lastError = ""; - int dataStatus; - ByteString data; - ByteString userID = ByteString::Build(authUser.UserID); - if (authUser.UserID) - { - if (!save.GetGameSave()) - { - lastError = "Empty game save"; - return RequestFailure; - } - - save.SetID(0); - - auto [ fromNewerVersion, gameData ] = save.GetGameSave()->Serialise(); - (void)fromNewerVersion; - - if (!gameData.size()) - { - lastError = "Cannot serialize game save"; - return RequestFailure; - } -#if defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0 - else if (fromNewerVersion && save.GetPublished()) - { - lastError = "Cannot publish save, incompatible with latest release version."; - return RequestFailure; - } -#endif - - data = http::Request::SimpleAuth(SCHEME SERVER "/Save.api", &dataStatus, userID, authUser.SessionID, { - { "Name", save.GetName().ToUtf8() }, - { "Description", save.GetDescription().ToUtf8() }, - { "Data:save.bin", ByteString(gameData.begin(), gameData.end()) }, - { "Publish", save.GetPublished() ? "Public" : "Private" }, - { "Key", authUser.SessionKey } - }); - } - else - { - lastError = "Not authenticated"; - return RequestFailure; - } - - RequestStatus ret = ParseServerReturn(data, dataStatus, false); - if (ret == RequestOkay) - { - int saveID = ByteString(data.begin() + 3, data.end()).ToNumber(); - if (!saveID) - { - lastError = "Server did not return Save ID"; - ret = RequestFailure; - } - else - save.SetID(saveID); - } - return ret; -} - void Client::MoveStampToFront(ByteString stampID) { - for (std::list::iterator iterator = stampIDs.begin(), end = stampIDs.end(); iterator != end; ++iterator) + auto it = std::find(stampIDs.begin(), stampIDs.end(), stampID); + auto changed = false; + if (it == stampIDs.end()) { - if((*iterator) == stampID) - { - stampIDs.erase(iterator); - break; - } + stampIDs.push_back(stampID); + it = stampIDs.end() - 1; + changed = true; + } + else if (it != stampIDs.begin()) + { + changed = true; + } + if (changed) + { + std::rotate(stampIDs.begin(), it, it + 1); + WriteStamps(); } - stampIDs.push_front(stampID); - updateStamps(); } -SaveFile * Client::GetStamp(ByteString stampID) +std::unique_ptr Client::GetStamp(ByteString stampID) { - ByteString stampFile = ByteString(STAMPS_DIR PATH_SEP + stampID + ".stm"); - SaveFile *saveFile = LoadSaveFile(stampFile); + ByteString stampFile = ByteString(ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm")); + auto saveFile = LoadSaveFile(stampFile); if (!saveFile) saveFile = LoadSaveFile(stampID); else @@ -581,32 +297,38 @@ SaveFile * Client::GetStamp(ByteString stampID) void Client::DeleteStamp(ByteString stampID) { - for (std::list::iterator iterator = stampIDs.begin(), end = stampIDs.end(); iterator != end; ++iterator) + auto it = std::remove(stampIDs.begin(), stampIDs.end(), stampID); + if (it != stampIDs.end()) { - if ((*iterator) == stampID) - { - ByteString stampFilename = ByteString::Build(STAMPS_DIR, PATH_SEP, stampID, ".stm"); - remove(stampFilename.c_str()); - stampIDs.erase(iterator); - break; - } + stampIDs.erase(it, stampIDs.end()); + Platform::RemoveFile(ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, stampID, ".stm")); + WriteStamps(); } - - updateStamps(); } -ByteString Client::AddStamp(GameSave * saveData) +ByteString Client::AddStamp(std::unique_ptr saveData) { - unsigned t=(unsigned)time(NULL); - if (lastStampTime!=t) + auto now = (uint64_t)time(NULL); + if (lastStampTime != now) { - lastStampTime=t; - lastStampName=0; + lastStampTime = now; + lastStampName = 0; } else - lastStampName++; - ByteString saveID = ByteString::Build(Format::Hex(Format::Width(lastStampTime, 8)), Format::Hex(Format::Width(lastStampName, 2))); - ByteString filename = STAMPS_DIR PATH_SEP + saveID + ".stm"; + { + lastStampName += 1; + } + ByteString saveID, filename; + while (true) + { + saveID = ByteString::Build(Format::Hex(Format::Width(lastStampTime, 8)), Format::Hex(Format::Width(lastStampName, 2))); + filename = ByteString::Build(STAMPS_DIR, PATH_SEP_CHAR, saveID, ".stm"); + if (!Platform::FileExists(filename)) + { + break; + } + lastStampName += 1; + } Platform::MakeDirectory(STAMPS_DIR); @@ -614,7 +336,7 @@ ByteString Client::AddStamp(GameSave * saveData) stampInfo["type"] = "stamp"; stampInfo["username"] = authUser.Username; stampInfo["name"] = filename; - stampInfo["date"] = (Json::Value::UInt64)time(NULL); + stampInfo["date"] = Json::Value::UInt64(now); if (authors.size() != 0) { // This is a stamp, always append full authorship info (even if same user) @@ -628,392 +350,76 @@ ByteString Client::AddStamp(GameSave * saveData) return ""; Platform::WriteFile(gameData, filename); - - stampIDs.push_front(saveID); - - updateStamps(); - + MoveStampToFront(saveID); return saveID; } -void Client::updateStamps() -{ - Platform::MakeDirectory(STAMPS_DIR); - - std::ofstream stampsStream; - stampsStream.open(ByteString(STAMPS_DIR PATH_SEP "stamps.def").c_str(), std::ios::binary); - for (std::list::const_iterator iterator = stampIDs.begin(), end = stampIDs.end(); iterator != end; ++iterator) - { - stampsStream.write((*iterator).c_str(), 10); - } - stampsStream.write("\0", 1); - stampsStream.close(); - return; -} - void Client::RescanStamps() { - stampIDs.clear(); - for (auto &stamp : Platform::DirectorySearch("stamps", "", { ".stm" })) + ByteString extension = ".stm"; + std::set stampFilesSet; + for (auto &stampID : Platform::DirectorySearch("stamps", "", { extension })) { - if (stamp.size() == 14) + stampFilesSet.insert(stampID.substr(0, stampID.size() - extension.size())); + } + std::vector newStampIDs; + auto changed = false; + for (auto &stampID : stampIDs) + { + if (stampFilesSet.find(stampID) == stampFilesSet.end()) { - stampIDs.push_front(stamp.Substr(0, 10)); + changed = true; + } + else + { + newStampIDs.push_back(stampID); } } - stampIDs.sort(std::greater()); - updateStamps(); -} - -int Client::GetStampsCount() -{ - return stampIDs.size(); -} - -std::vector Client::GetStamps(int start, int count) -{ - int size = (int)stampIDs.size(); - if (start+count > size) + auto oldCount = newStampIDs.size(); + auto stampIDsSet = std::set(stampIDs.begin(), stampIDs.end()); + for (auto &stampID : stampFilesSet) { - if(start > size) - return std::vector(); - count = size-start; - } - - std::vector stampRange; - int index = 0; - for (std::list::const_iterator iterator = stampIDs.begin(), end = stampIDs.end(); iterator != end; ++iterator, ++index) - { - if(index>=start && index < start+count) - stampRange.push_back(*iterator); - } - return stampRange; -} - -RequestStatus Client::ExecVote(int saveID, int direction) -{ - lastError = ""; - int dataStatus; - ByteString data; - - if (authUser.UserID) - { - ByteString saveIDText = ByteString::Build(saveID); - ByteString userIDText = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(SCHEME SERVER "/Vote.api", &dataStatus, userIDText, authUser.SessionID, { - { "ID", saveIDText }, - { "Action", direction ? (direction == 1 ? "Up" : "Down") : "Reset" }, - { "Key", authUser.SessionKey } - }); - } - else - { - lastError = "Not authenticated"; - return RequestFailure; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, false); - return ret; -} - -std::vector Client::GetSaveData(int saveID, int saveDate) -{ - lastError = ""; - int dataStatus; - ByteString data; - ByteString urlStr; - if (saveDate) - urlStr = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_", saveDate, ".cps"); - else - urlStr = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, ".cps"); - - data = http::Request::Simple(urlStr, &dataStatus); - - // will always return failure - ParseServerReturn(data, dataStatus, false); - if (data.size() && dataStatus == 200) - { - return std::vector(data.begin(), data.end()); - } - return {}; -} - -LoginStatus Client::Login(ByteString username, ByteString password, User & user) -{ - lastError = ""; - - user.UserID = 0; - user.Username = ""; - user.SessionID = ""; - user.SessionKey = ""; - - ByteString data; - int dataStatus; - data = http::Request::Simple("https://" SERVER "/Login.json", &dataStatus, { - { "name", username }, - { "pass", password }, - }); - - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - if (ret == RequestOkay) - { - try + if (stampIDsSet.find(stampID) == stampIDsSet.end()) { - std::istringstream dataStream(data); - Json::Value objDocument; - dataStream >> objDocument; - - ByteString usernameTemp = objDocument["Username"].asString(); - int userIDTemp = objDocument["UserID"].asInt(); - ByteString sessionIDTemp = objDocument["SessionID"].asString(); - ByteString sessionKeyTemp = objDocument["SessionKey"].asString(); - ByteString userElevationTemp = objDocument["Elevation"].asString(); - - Json::Value notificationsArray = objDocument["Notifications"]; - for (Json::UInt j = 0; j < notificationsArray.size(); j++) - { - ByteString notificationLink = notificationsArray[j]["Link"].asString(); - String notificationText = ByteString(notificationsArray[j]["Text"].asString()).FromUtf8(); - - std::pair item = std::pair(notificationText, notificationLink); - AddServerNotification(item); - } - - user.Username = usernameTemp; - user.UserID = userIDTemp; - user.SessionID = sessionIDTemp; - user.SessionKey = sessionKeyTemp; - ByteString userElevation = userElevationTemp; - if(userElevation == "Admin") - user.UserElevation = User::ElevationAdmin; - else if(userElevation == "Mod") - user.UserElevation = User::ElevationModerator; - else - user.UserElevation= User::ElevationNone; - return LoginOkay; - } - catch (std::exception &e) - { - lastError = "Could not read response: " + ByteString(e.what()).FromUtf8(); - return LoginError; + newStampIDs.push_back(stampID); + changed = true; } } - return LoginError; + if (changed) + { + // Move newly discovered stamps to front. + std::rotate(newStampIDs.begin(), newStampIDs.begin() + oldCount, newStampIDs.end()); + stampIDs = newStampIDs; + WriteStamps(); + } } -RequestStatus Client::DeleteSave(int saveID) +void Client::WriteStamps() { - lastError = ""; - ByteString data; - ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Delete&Key=", authUser.SessionKey); - int dataStatus; - if(authUser.UserID) + if (stampIDs.size()) { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID); + stamps->Set("MostRecentlyUsedFirst", stampIDs); } - else - { - lastError = "Not authenticated"; - return RequestFailure; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - return ret; } -RequestStatus Client::AddComment(int saveID, String comment) +const std::vector &Client::GetStamps() const { - lastError = ""; - ByteString data; - int dataStatus; - ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID); - if(authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID, { - { "Comment", comment.ToUtf8() }, - { "Key", authUser.SessionKey } - }); - } - else - { - lastError = "Not authenticated"; - return RequestFailure; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - return ret; + return stampIDs; } -RequestStatus Client::FavouriteSave(int saveID, bool favourite) -{ - lastError = ""; - ByteStringBuilder urlStream; - ByteString data; - int dataStatus; - urlStream << SCHEME << SERVER << "/Browse/Favourite.json?ID=" << saveID << "&Key=" << authUser.SessionKey; - if(!favourite) - urlStream << "&Mode=Remove"; - if(authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(urlStream.Build(), &dataStatus, userID, authUser.SessionID); - } - else - { - lastError = "Not authenticated"; - return RequestFailure; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - return ret; -} - -RequestStatus Client::ReportSave(int saveID, String message) -{ - lastError = ""; - ByteString data; - int dataStatus; - ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Report.json?ID=", saveID, "&Key=", authUser.SessionKey); - if(authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID, { - { "Reason", message.ToUtf8() }, - }); - } - else - { - lastError = "Not authenticated"; - return RequestFailure; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - return ret; -} - -RequestStatus Client::UnpublishSave(int saveID) -{ - lastError = ""; - ByteString data; - int dataStatus; - ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Unpublish&Key=", authUser.SessionKey); - if(authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID); - } - else - { - lastError = "Not authenticated"; - return RequestFailure; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - return ret; -} - -RequestStatus Client::PublishSave(int saveID) -{ - lastError = ""; - ByteString data; - int dataStatus; - ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/View.json?ID=", saveID, "&Key=", authUser.SessionKey); - if (authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID, { - { "ActionPublish", "bagels" }, - }); - } - else - { - lastError = "Not authenticated"; - return RequestFailure; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - return ret; -} - -SaveInfo * Client::GetSave(int saveID, int saveDate) -{ - lastError = ""; - ByteStringBuilder urlStream; - urlStream << SCHEME << SERVER << "/Browse/View.json?ID=" << saveID; - if(saveDate) - { - urlStream << "&Date=" << saveDate; - } - ByteString data; - int dataStatus; - if(authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - - data = http::Request::SimpleAuth(urlStream.Build(), &dataStatus, userID, authUser.SessionID); - } - else - { - data = http::Request::Simple(urlStream.Build(), &dataStatus); - } - if(dataStatus == 200 && data.size()) - { - try - { - std::istringstream dataStream(data); - Json::Value objDocument; - dataStream >> objDocument; - - int tempID = objDocument["ID"].asInt(); - int tempScoreUp = objDocument["ScoreUp"].asInt(); - int tempScoreDown = objDocument["ScoreDown"].asInt(); - int tempMyScore = objDocument["ScoreMine"].asInt(); - ByteString tempUsername = objDocument["Username"].asString(); - String tempName = ByteString(objDocument["Name"].asString()).FromUtf8(); - String tempDescription = ByteString(objDocument["Description"].asString()).FromUtf8(); - int tempCreatedDate = objDocument["DateCreated"].asInt(); - int tempUpdatedDate = objDocument["Date"].asInt(); - bool tempPublished = objDocument["Published"].asBool(); - bool tempFavourite = objDocument["Favourite"].asBool(); - int tempComments = objDocument["Comments"].asInt(); - int tempViews = objDocument["Views"].asInt(); - int tempVersion = objDocument["Version"].asInt(); - - Json::Value tagsArray = objDocument["Tags"]; - std::list tempTags; - for (Json::UInt j = 0; j < tagsArray.size(); j++) - tempTags.push_back(tagsArray[j].asString()); - - SaveInfo * tempSave = new SaveInfo(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, - tempScoreDown, tempMyScore, tempUsername, tempName, - tempDescription, tempPublished, tempTags); - tempSave->Comments = tempComments; - tempSave->Favourite = tempFavourite; - tempSave->Views = tempViews; - tempSave->Version = tempVersion; - return tempSave; - } - catch (std::exception & e) - { - lastError = "Could not read response: " + ByteString(e.what()).FromUtf8(); - return NULL; - } - } - else - { - lastError = http::StatusText(dataStatus); - } - return NULL; -} - -SaveFile * Client::LoadSaveFile(ByteString filename) +std::unique_ptr Client::LoadSaveFile(ByteString filename) { ByteString err; - SaveFile *file = nullptr; + std::unique_ptr file; if (Platform::FileExists(filename)) { - file = new SaveFile(filename); + file = std::make_unique(filename); try { std::vector data; if (Platform::ReadFile(data, filename)) { - file->SetGameSave(new GameSave(std::move(data))); + file->SetGameSave(std::make_unique(std::move(data))); } else { @@ -1036,207 +442,11 @@ SaveFile * Client::LoadSaveFile(ByteString filename) { file->SetLoadingError(err.FromUtf8()); } -#ifdef LUACONSOLE - luacon_ci->SetLastError(err.FromUtf8()); -#endif + commandInterface->SetLastError(err.FromUtf8()); } return file; } -std::vector > * Client::GetTags(int start, int count, String query, int & resultCount) -{ - lastError = ""; - resultCount = 0; - std::vector > * tagArray = new std::vector >(); - ByteStringBuilder urlStream; - ByteString data; - int dataStatus; - urlStream << SCHEME << SERVER << "/Browse/Tags.json?Start=" << start << "&Count=" << count; - if(query.length()) - { - urlStream << "&Search_Query="; - if(query.length()) - urlStream << format::URLEncode(query.ToUtf8()); - } - - data = http::Request::Simple(urlStream.Build(), &dataStatus); - if(dataStatus == 200 && data.size()) - { - try - { - std::istringstream dataStream(data); - Json::Value objDocument; - dataStream >> objDocument; - - resultCount = objDocument["TagTotal"].asInt(); - Json::Value tagsArray = objDocument["Tags"]; - for (Json::UInt j = 0; j < tagsArray.size(); j++) - { - int tagCount = tagsArray[j]["Count"].asInt(); - ByteString tag = tagsArray[j]["Tag"].asString(); - tagArray->push_back(std::pair(tag, tagCount)); - } - } - catch (std::exception & e) - { - lastError = "Could not read response: " + ByteString(e.what()).FromUtf8(); - } - } - else - { - lastError = http::StatusText(dataStatus); - } - return tagArray; -} - -std::vector * Client::SearchSaves(int start, int count, String query, ByteString sort, ByteString category, int & resultCount) -{ - lastError = ""; - resultCount = 0; - std::vector * saveArray = new std::vector(); - ByteStringBuilder urlStream; - ByteString data; - int dataStatus; - urlStream << SCHEME << SERVER << "/Browse.json?Start=" << start << "&Count=" << count; - if(query.length() || sort.length()) - { - urlStream << "&Search_Query="; - if(query.length()) - urlStream << format::URLEncode(query.ToUtf8()); - if(sort == "date") - { - if(query.length()) - urlStream << format::URLEncode(" "); - urlStream << format::URLEncode("sort:") << format::URLEncode(sort); - } - } - if(category.length()) - { - urlStream << "&Category=" << format::URLEncode(category); - } - if(authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(urlStream.Build(), &dataStatus, userID, authUser.SessionID); - } - else - { - data = http::Request::Simple(urlStream.Build(), &dataStatus); - } - ParseServerReturn(data, dataStatus, true); - if (dataStatus == 200 && data.size()) - { - try - { - std::istringstream dataStream(data); - Json::Value objDocument; - dataStream >> objDocument; - - resultCount = objDocument["Count"].asInt(); - Json::Value savesArray = objDocument["Saves"]; - for (Json::UInt j = 0; j < savesArray.size(); j++) - { - int tempID = savesArray[j]["ID"].asInt(); - int tempCreatedDate = savesArray[j]["Created"].asInt(); - int tempUpdatedDate = savesArray[j]["Updated"].asInt(); - int tempScoreUp = savesArray[j]["ScoreUp"].asInt(); - int tempScoreDown = savesArray[j]["ScoreDown"].asInt(); - ByteString tempUsername = savesArray[j]["Username"].asString(); - String tempName = ByteString(savesArray[j]["Name"].asString()).FromUtf8(); - int tempVersion = savesArray[j]["Version"].asInt(); - bool tempPublished = savesArray[j]["Published"].asBool(); - SaveInfo * tempSaveInfo = new SaveInfo(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, tempScoreDown, tempUsername, tempName); - tempSaveInfo->Version = tempVersion; - tempSaveInfo->SetPublished(tempPublished); - saveArray->push_back(tempSaveInfo); - } - } - catch (std::exception &e) - { - lastError = "Could not read response: " + ByteString(e.what()).FromUtf8(); - } - } - return saveArray; -} - -std::list * Client::RemoveTag(int saveID, ByteString tag) -{ - lastError = ""; - std::list * tags = NULL; - ByteString data; - int dataStatus; - ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=delete&ID=", saveID, "&Tag=", tag, "&Key=", authUser.SessionKey); - if(authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID); - } - else - { - lastError = "Not authenticated"; - return NULL; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - if (ret == RequestOkay) - { - try - { - std::istringstream dataStream(data); - Json::Value responseObject; - dataStream >> responseObject; - - Json::Value tagsArray = responseObject["Tags"]; - tags = new std::list(); - for (Json::UInt j = 0; j < tagsArray.size(); j++) - tags->push_back(tagsArray[j].asString()); - } - catch (std::exception &e) - { - lastError = "Could not read response: " + ByteString(e.what()).FromUtf8(); - } - } - return tags; -} - -std::list * Client::AddTag(int saveID, ByteString tag) -{ - lastError = ""; - std::list * tags = NULL; - ByteString data; - int dataStatus; - ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=add&ID=", saveID, "&Tag=", tag, "&Key=", authUser.SessionKey); - if(authUser.UserID) - { - ByteString userID = ByteString::Build(authUser.UserID); - data = http::Request::SimpleAuth(url, &dataStatus, userID, authUser.SessionID); - } - else - { - lastError = "Not authenticated"; - return NULL; - } - RequestStatus ret = ParseServerReturn(data, dataStatus, true); - if (ret == RequestOkay) - { - try - { - std::istringstream dataStream(data); - Json::Value responseObject; - dataStream >> responseObject; - - Json::Value tagsArray = responseObject["Tags"]; - tags = new std::list(); - for (Json::UInt j = 0; j < tagsArray.size(); j++) - tags->push_back(tagsArray[j].asString()); - } - catch (std::exception & e) - { - lastError = "Could not read response: " + ByteString(e.what()).FromUtf8(); - } - } - return tags; -} - // stamp-specific wrapper for MergeAuthorInfo // also used for clipboard and lua stamps void Client::MergeStampAuthorInfo(Json::Value stampAuthors) @@ -1303,392 +513,163 @@ void Client::SaveAuthorInfo(Json::Value *saveInto) } } -// powder.pref preference getting / setting functions - -// Recursively go down the json to get the setting we want -Json::Value Client::GetPref(Json::Value root, ByteString prop, Json::Value defaultValue) +bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor) { - try + auto &prefs = GlobalPrefs::Ref(); + auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector{}); + std::vector newCustomGOLTypes; + bool nameTaken = false; + for (auto gol : customGOLTypes) { - if(ByteString::Split split = prop.SplitBy('.')) - return GetPref(root[split.Before()], split.After(), defaultValue); - else - return root.get(prop, defaultValue); - } - catch (std::exception & e) - { - return defaultValue; - } -} - -ByteString Client::GetPrefByteString(ByteString prop, ByteString defaultValue) -{ - try - { - return GetPref(preferences, prop, defaultValue).asString(); - } - catch (std::exception & e) - { - return defaultValue; - } -} - -String Client::GetPrefString(ByteString prop, String defaultValue) -{ - try - { - return ByteString(GetPref(preferences, prop, defaultValue.ToUtf8()).asString()).FromUtf8(false); - } - catch (std::exception & e) - { - return defaultValue; - } -} - -double Client::GetPrefNumber(ByteString prop, double defaultValue) -{ - try - { - return GetPref(preferences, prop, defaultValue).asDouble(); - } - catch (std::exception & e) - { - return defaultValue; - } -} - -int Client::GetPrefInteger(ByteString prop, int defaultValue) -{ - try - { - return GetPref(preferences, prop, defaultValue).asInt(); - } - catch (std::exception & e) - { - return defaultValue; - } -} - -unsigned int Client::GetPrefUInteger(ByteString prop, unsigned int defaultValue) -{ - try - { - return GetPref(preferences, prop, defaultValue).asUInt(); - } - catch (std::exception & e) - { - return defaultValue; - } -} - -bool Client::GetPrefBool(ByteString prop, bool defaultValue) -{ - try - { - return GetPref(preferences, prop, defaultValue).asBool(); - } - catch (std::exception & e) - { - return defaultValue; - } -} - -std::vector Client::GetPrefByteStringArray(ByteString prop) -{ - try - { - std::vector ret; - Json::Value arr = GetPref(preferences, prop); - for (int i = 0; i < (int)arr.size(); i++) - ret.push_back(arr[i].asString()); - return ret; - } - catch (std::exception & e) - { - - } - return std::vector(); -} - -std::vector Client::GetPrefStringArray(ByteString prop) -{ - try - { - std::vector ret; - Json::Value arr = GetPref(preferences, prop); - for (int i = 0; i < (int)arr.size(); i++) - ret.push_back(ByteString(arr[i].asString()).FromUtf8(false)); - return ret; - } - catch (std::exception & e) - { - - } - return std::vector(); -} - -std::vector Client::GetPrefNumberArray(ByteString prop) -{ - try - { - std::vector ret; - Json::Value arr = GetPref(preferences, prop); - for (int i = 0; i < (int)arr.size(); i++) - ret.push_back(arr[i].asDouble()); - return ret; - } - catch (std::exception & e) - { - - } - return std::vector(); -} - -std::vector Client::GetPrefIntegerArray(ByteString prop) -{ - try - { - std::vector ret; - Json::Value arr = GetPref(preferences, prop); - for (int i = 0; i < (int)arr.size(); i++) - ret.push_back(arr[i].asInt()); - return ret; - } - catch (std::exception & e) - { - - } - return std::vector(); -} - -std::vector Client::GetPrefUIntegerArray(ByteString prop) -{ - try - { - std::vector ret; - Json::Value arr = GetPref(preferences, prop); - for (int i = 0; i < (int)arr.size(); i++) - ret.push_back(arr[i].asUInt()); - return ret; - } - catch (std::exception & e) - { - - } - return std::vector(); -} - -std::vector Client::GetPrefBoolArray(ByteString prop) -{ - try - { - std::vector ret; - Json::Value arr = GetPref(preferences, prop); - for (int i = 0; i < (int)arr.size(); i++) - ret.push_back(arr[i].asBool()); - return ret; - } - catch (std::exception & e) - { - - } - return std::vector(); -} - -// Helper preference setting function. -// To actually save any changes to preferences, we need to directly do preferences[property] = thing -// any other way will set the value of a copy of preferences, not the original -// This function will recursively go through and create an object with the property we wanted set, -// and return it to SetPref to do the actual setting -Json::Value Client::SetPrefHelper(Json::Value root, ByteString prop, Json::Value value) -{ - if(ByteString::Split split = prop.SplitBy('.')) - { - Json::Value toSet = GetPref(root, split.Before()); - toSet = SetPrefHelper(toSet, split.After(), value); - root[split.Before()] = toSet; - } - else - root[prop] = value; - return root; -} - -void Client::SetPref(ByteString prop, Json::Value value) -{ - try - { - if(ByteString::Split split = prop.SplitBy('.')) - preferences[split.Before()] = SetPrefHelper(preferences[split.Before()], split.After(), value); - else - preferences[prop] = value; - WritePrefs(); - } - catch (std::exception & e) - { - - } -} - -void Client::SetPref(ByteString prop, std::vector value) -{ - try - { - Json::Value arr; - for (int i = 0; i < (int)value.size(); i++) + auto parts = gol.FromUtf8().PartitionBy(' '); + if (parts.size()) { - arr.append(value[i]); - } - SetPref(prop, arr); - } - catch (std::exception & e) - { - - } -} - -void Client::SetPrefUnicode(ByteString prop, String value) -{ - SetPref(prop, value.ToUtf8()); -} - -bool Client::DoInstallation() -{ - bool ok = true; -#if defined(WIN) - auto deleteKey = [](ByteString path) { - RegDeleteKeyW(HKEY_CURRENT_USER, Platform::WinWiden(path).c_str()); - }; - auto createKey = [](ByteString path, ByteString value, ByteString extraKey = {}, ByteString extraValue = {}) { - auto ok = true; - auto wPath = Platform::WinWiden(path); - auto wValue = Platform::WinWiden(value); - auto wExtraKey = Platform::WinWiden(extraKey); - auto wExtraValue = Platform::WinWiden(extraValue); - HKEY k; - ok = ok && RegCreateKeyExW(HKEY_CURRENT_USER, wPath.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &k, NULL) == ERROR_SUCCESS; - ok = ok && RegSetValueExW(k, NULL, 0, REG_SZ, reinterpret_cast(wValue.c_str()), (wValue.size() + 1) * 2) == ERROR_SUCCESS; - if (wExtraKey.size()) - { - ok = ok && RegSetValueExW(k, wExtraKey.c_str(), 0, REG_SZ, reinterpret_cast(wExtraValue.c_str()), (wExtraValue.size() + 1) * 2) == ERROR_SUCCESS; - } - RegCloseKey(k); - return ok; - }; - - CoInitializeEx(NULL, COINIT_MULTITHREADED); - auto exe = Platform::ExecutableName(); -#ifndef IDI_DOC_ICON - // make this fail so I don't remove #include "resource.h" again and get away with it -# error where muh IDI_DOC_ICON D: -#endif - auto icon = exe + ",-" MTOS(IDI_DOC_ICON); - auto path = Platform::GetCwd(); - auto open = ByteString::Build("\"", exe, "\" ddir \"", path, "\" \"file://%1\""); - auto ptsave = ByteString::Build("\"", exe, "\" ddir \"", path, "\" \"%1\""); - deleteKey("Software\\Classes\\ptsave"); - deleteKey("Software\\Classes\\.cps"); - deleteKey("Software\\Classes\\.stm"); - deleteKey("Software\\Classes\\PowderToySave"); - ok = ok && createKey("Software\\Classes\\ptsave", "Powder Toy Save", "URL Protocol", ""); - ok = ok && createKey("Software\\Classes\\ptsave\\DefaultIcon", icon); - ok = ok && createKey("Software\\Classes\\ptsave\\shell\\open\\command", ptsave); - ok = ok && createKey("Software\\Classes\\.cps", "PowderToySave"); - ok = ok && createKey("Software\\Classes\\.stm", "PowderToySave"); - ok = ok && createKey("Software\\Classes\\PowderToySave", "Powder Toy Save"); - ok = ok && createKey("Software\\Classes\\PowderToySave\\DefaultIcon", icon); - ok = ok && createKey("Software\\Classes\\PowderToySave\\shell\\open\\command", open); - IShellLinkW *shellLink = NULL; - IPersistFile *shellLinkPersist = NULL; - wchar_t programsPath[MAX_PATH]; - ok = ok && SHGetFolderPathW(NULL, CSIDL_PROGRAMS, NULL, SHGFP_TYPE_CURRENT, programsPath) == S_OK; - ok = ok && CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&shellLink) == S_OK; - ok = ok && shellLink->SetPath(Platform::WinWiden(exe).c_str()) == S_OK; - ok = ok && shellLink->SetWorkingDirectory(Platform::WinWiden(path).c_str()) == S_OK; - ok = ok && shellLink->SetDescription(Platform::WinWiden(APPNAME).c_str()) == S_OK; - ok = ok && shellLink->QueryInterface(IID_IPersistFile, (LPVOID *)&shellLinkPersist) == S_OK; - ok = ok && shellLinkPersist->Save(Platform::WinWiden(Platform::WinNarrow(programsPath) + "\\" APPNAME ".lnk").c_str(), TRUE) == S_OK; - if (shellLinkPersist) - { - shellLinkPersist->Release(); - } - if (shellLink) - { - shellLink->Release(); - } - CoUninitialize(); -#elif defined(LIN) - auto desktopEscapeString = [](ByteString str) { - ByteString escaped; - for (auto ch : str) - { - auto from = " " "\n" "\t" "\r" "\\"; - auto to = "s" "n" "t" "r" "\\"; - if (auto off = strchr(from, ch)) + if (parts[0] == nameString) { - escaped.append(1, '\\'); - escaped.append(1, to[off - from]); + nameTaken = true; + } + } + newCustomGOLTypes.push_back(gol); + } + if (nameTaken) + return false; + + StringBuilder sb; + sb << nameString << " " << ruleString << " " << highColor << " " << lowColor; + newCustomGOLTypes.push_back(sb.Build().ToUtf8()); + prefs.Set("CustomGOL.Types", newCustomGOLTypes); + return true; +} + +String Client::DoMigration(ByteString fromDir, ByteString toDir) +{ + if (fromDir.at(fromDir.length() - 1) != '/') + fromDir = fromDir + '/'; + if (toDir.at(toDir.length() - 1) != '/') + toDir = toDir + '/'; + + std::ofstream logFile(fromDir + "/migrationlog.txt", std::ios::out); + logFile << "Running migration of data from " << fromDir + " to " << toDir << std::endl; + + // Get lists of files to migrate + auto stamps = Platform::DirectorySearch(fromDir + "stamps", "", { ".stm" }); + auto saves = Platform::DirectorySearch(fromDir + "Saves", "", { ".cps", ".stm" }); + auto scripts = Platform::DirectorySearch(fromDir + "scripts", "", { ".lua", ".txt" }); + auto downloadedScripts = Platform::DirectorySearch(fromDir + "scripts/downloaded", "", { ".lua" }); + bool hasScriptinfo = Platform::FileExists(toDir + "scripts/downloaded/scriptinfo"); + auto screenshots = Platform::DirectorySearch(fromDir, "screenshot", { ".png" }); + bool hasAutorun = Platform::FileExists(fromDir + "autorun.lua"); + bool hasPref = Platform::FileExists(fromDir + "powder.pref"); + + if (stamps.empty() && saves.empty() && scripts.empty() && downloadedScripts.empty() && screenshots.empty() && !hasAutorun && !hasPref) + { + logFile << "Nothing to migrate."; + return "Nothing to migrate. This button is used to migrate data from pre-96.0 TPT installations to the shared directory"; + } + + StringBuilder result; + std::stack dirsToDelete; + + // Migrate a list of files + auto migrateList = [&](std::vector list, ByteString directory, String niceName) { + result << '\n' << niceName << ": "; + if (!list.empty() && !directory.empty()) + Platform::MakeDirectory(toDir + directory); + int migratedCount = 0, failedCount = 0; + for (auto &item : list) + { + std::string from = fromDir + directory + "/" + item; + std::string to = toDir + directory + "/" + item; + if (!Platform::FileExists(to)) + { + if (Platform::RenameFile(from, to, false)) + { + failedCount++; + logFile << "failed to move " << from << " to " << to << std::endl; + } + else + { + migratedCount++; + logFile << "moved " << from << " to " << to << std::endl; + } } else { - escaped.append(1, ch); + logFile << "skipping " << from << "(already exists)" << std::endl; } } - return escaped; - }; - auto desktopEscapeExec = [](ByteString str) { - ByteString escaped; - for (auto ch : str) - { - if (strchr(" \t\n\"\'\\><~|&;$*?#()`", ch)) - { - escaped.append(1, '\\'); - } - escaped.append(1, ch); - } - return escaped; + + dirsToDelete.push(directory); + result << "\bt" << migratedCount << " migratated\x0E, \br" << failedCount << " failed\x0E"; + int duplicates = list.size() - migratedCount - failedCount; + if (duplicates) + result << ", " << list.size() - migratedCount - failedCount << " skipped (duplicate)"; }; - if (ok) + // Migrate a single file + auto migrateFile = [&fromDir, &toDir, &result, &logFile](ByteString filename) { + ByteString from = fromDir + filename; + ByteString to = toDir + filename; + if (!Platform::FileExists(to)) + { + if (Platform::RenameFile(from, to, false)) + { + logFile << "failed to move " << from << " to " << to << std::endl; + result << "\n\br" << filename.FromUtf8() << " migration failed\x0E"; + } + else + { + logFile << "moved " << from << " to " << to << std::endl; + result << '\n' << filename.FromUtf8() << " migrated"; + } + } + else + { + logFile << "skipping " << from << "(already exists)" << std::endl; + result << '\n' << filename.FromUtf8() << " skipped (already exists)"; + } + + if (!Platform::RemoveFile(fromDir + filename)) { + logFile << "failed to delete " << filename << std::endl; + } + }; + + // Do actual migration + Platform::RemoveFile(fromDir + "stamps/stamps.def"); + Platform::RemoveFile(fromDir + "stamps/stamps.json"); + migrateList(stamps, "stamps", "Stamps"); + migrateList(saves, "Saves", "Saves"); + if (!scripts.empty()) + migrateList(scripts, "scripts", "Scripts"); + if (!hasScriptinfo && !downloadedScripts.empty()) { - ByteString desktopData(powder_desktop, powder_desktop + powder_desktop_size); - auto exe = Platform::ExecutableName(); - auto path = exe.SplitFromEndBy('/').Before(); - desktopData = desktopData.Substitute("Exec=" APPEXE, "Exec=" + desktopEscapeString(desktopEscapeExec(exe))); - desktopData += ByteString::Build("Path=", desktopEscapeString(path), "\n"); - ByteString file = APPVENDOR "-" APPID ".desktop"; - ok = ok && Platform::WriteFile(std::vector(desktopData.begin(), desktopData.end()), file); - ok = ok && !system(ByteString::Build("xdg-desktop-menu install ", file).c_str()); - ok = ok && !system(ByteString::Build("xdg-mime default ", file, " application/vnd.powdertoy.save").c_str()); - ok = ok && !system(ByteString::Build("xdg-mime default ", file, " x-scheme-handler/ptsave").c_str()); - Platform::RemoveFile(file); + migrateList(downloadedScripts, "scripts/downloaded", "Downloaded scripts"); + migrateFile("scripts/downloaded/scriptinfo"); } - if (ok) + if (!screenshots.empty()) + migrateList(screenshots, "", "Screenshots"); + if (hasAutorun) + migrateFile("autorun.lua"); + if (hasPref) + migrateFile("powder.pref"); + + // Delete leftover directories + while (!dirsToDelete.empty()) { - ByteString file = APPVENDOR "-save.xml"; - ok = ok && Platform::WriteFile(std::vector(save_xml, save_xml + save_xml_size), file); - ok = ok && !system(ByteString::Build("xdg-mime install ", file).c_str()); - Platform::RemoveFile(file); + ByteString toDelete = dirsToDelete.top(); + if (!Platform::DeleteDirectory(fromDir + toDelete)) { + logFile << "failed to delete " << toDelete << std::endl; + } + dirsToDelete.pop(); } - if (ok) - { - ByteString file = APPVENDOR "-cps.png"; - ok = ok && Platform::WriteFile(std::vector(icon_cps_png, icon_cps_png + icon_cps_png_size), file); - ok = ok && !system(ByteString::Build("xdg-icon-resource install --noupdate --context mimetypes --size 64 ", file, " application-vnd.powdertoy.save").c_str()); - Platform::RemoveFile(file); - } - if (ok) - { - ByteString file = APPVENDOR "-exe.png"; - ok = ok && Platform::WriteFile(std::vector(icon_exe_png, icon_exe_png + icon_exe_png_size), file); - ok = ok && !system(ByteString::Build("xdg-icon-resource install --noupdate --size 64 ", file, " " APPVENDOR "-" APPEXE).c_str()); - Platform::RemoveFile(file); - } - if (ok) - { - ok = ok && !system("xdg-icon-resource forceupdate"); - } -#else - ok = false; -#endif - return ok; + + // chdir into the new directory + Platform::ChangeDir(toDir); + + RescanStamps(); + + logFile << std::endl << std::endl << "Migration complete. Results: " << result.Build().ToUtf8(); + logFile.close(); + + return result.Build(); } diff --git a/src/client/Client.h b/src/client/Client.h index 03505a0b5..9bfdb7d25 100644 --- a/src/client/Client.h +++ b/src/client/Client.h @@ -1,69 +1,42 @@ -#ifndef CLIENT_H -#define CLIENT_H -#include "Config.h" - -#include -#include - +#pragma once #include "common/String.h" -#include "common/Singleton.h" -#include - +#include "common/ExplicitSingleton.h" +#include "StartupInfo.h" #include "User.h" +#include +#include +#include +#include +#include class SaveInfo; class SaveFile; -class SaveComment; class GameSave; class VideoBuffer; -enum LoginStatus { - LoginOkay, LoginError -}; - -enum RequestStatus { - RequestOkay, RequestFailure -}; - -class UpdateInfo -{ -public: - enum BuildType { Stable, Beta, Snapshot }; - ByteString File; - String Changelog; - int Major; - int Minor; - int Build; - int Time; - BuildType Type; - UpdateInfo() : File(""), Changelog(""), Major(0), Minor(0), Build(0), Time(0), Type(Stable) {} - UpdateInfo(int major, int minor, int build, ByteString file, String changelog, BuildType type) : File(file), Changelog(changelog), Major(major), Minor(minor), Build(build), Time(0), Type(type) {} - UpdateInfo(int time, ByteString file, String changelog, BuildType type) : File(file), Changelog(changelog), Major(0), Minor(0), Build(0), Time(time), Type(type) {} -}; - +class Prefs; class RequestListener; class ClientListener; namespace http { - class Request; + class StartupRequest; } -class Client: public Singleton { +class Client: public ExplicitSingleton { private: String messageOfTheDay; - std::vector > serverNotifications; + std::vector serverNotifications; - http::Request *versionCheckRequest; - http::Request *alternateVersionCheckRequest; + std::unique_ptr versionCheckRequest; + std::unique_ptr alternateVersionCheckRequest; bool usingAltUpdateServer; bool updateAvailable; - UpdateInfo updateInfo; + std::optional updateInfo; - String lastError; bool firstRun; - std::list stampIDs; - unsigned lastStampTime; - int lastStampName; + std::vector stampIDs; + uint64_t lastStampTime = 0; + int lastStampName = 0; //Auth session User authUser; @@ -71,16 +44,15 @@ private: void notifyUpdateAvailable(); void notifyAuthUserChanged(); void notifyMessageOfTheDay(); - void notifyNewNotification(std::pair notification); - - // internal preferences handling - Json::Value preferences; - Json::Value GetPref(Json::Value root, ByteString prop, Json::Value defaultValue = Json::nullValue); - Json::Value SetPrefHelper(Json::Value root, ByteString prop, Json::Value value); + void notifyNewNotification(ServerNotification notification); // Save stealing info Json::Value authors; + std::unique_ptr stamps; + void MigrateStampsDef(); + void WriteStamps(); + public: std::vector listeners; @@ -94,7 +66,7 @@ public: void ClearAuthorInfo() { authors.clear(); } bool IsAuthorsEmpty() { return authors.size() == 0; } - UpdateInfo GetUpdateInfo(); + std::optional GetUpdateInfo(); Client(); ~Client(); @@ -102,80 +74,32 @@ public: ByteString FileOpenDialogue(); //std::string FileSaveDialogue(); - bool DoInstallation(); - - void AddServerNotification(std::pair notification); - std::vector > GetServerNotifications(); + void AddServerNotification(ServerNotification notification); + std::vector GetServerNotifications(); void SetMessageOfTheDay(String message); String GetMessageOfTheDay(); - void Initialise(ByteString proxy, ByteString cafile, ByteString capath, bool disableNetwork); + void Initialize(); bool IsFirstRun(); void AddListener(ClientListener * listener); void RemoveListener(ClientListener * listener); - RequestStatus ExecVote(int saveID, int direction); - RequestStatus UploadSave(SaveInfo & save); - - SaveFile * GetStamp(ByteString stampID); + std::unique_ptr GetStamp(ByteString stampID); void DeleteStamp(ByteString stampID); - ByteString AddStamp(GameSave * saveData); - std::vector GetStamps(int start, int count); + ByteString AddStamp(std::unique_ptr saveData); void RescanStamps(); - int GetStampsCount(); - SaveFile * GetFirstStamp(); + const std::vector &GetStamps() const; void MoveStampToFront(ByteString stampID); - void updateStamps(); - RequestStatus AddComment(int saveID, String comment); + std::unique_ptr LoadSaveFile(ByteString filename); - std::vector GetSaveData(int saveID, int saveDate); - - LoginStatus Login(ByteString username, ByteString password, User & user); - std::vector * SearchSaves(int start, int count, String query, ByteString sort, ByteString category, int & resultCount); - std::vector > * GetTags(int start, int count, String query, int & resultCount); - - SaveInfo * GetSave(int saveID, int saveDate); - SaveFile * LoadSaveFile(ByteString filename); - - RequestStatus DeleteSave(int saveID); - RequestStatus ReportSave(int saveID, String message); - RequestStatus UnpublishSave(int saveID); - RequestStatus PublishSave(int saveID); - RequestStatus FavouriteSave(int saveID, bool favourite); void SetAuthUser(User user); User GetAuthUser(); - std::list * RemoveTag(int saveID, ByteString tag); //TODO RequestStatus - std::list * AddTag(int saveID, ByteString tag); - String GetLastError() { - return lastError; - } - RequestStatus ParseServerReturn(ByteString &result, int status, bool json); void Tick(); - bool CheckUpdate(http::Request *updateRequest, bool checkSession); - void Shutdown(); - - // preferences functions - void WritePrefs(); - - ByteString GetPrefByteString(ByteString prop, ByteString defaultValue); - String GetPrefString(ByteString prop, String defaultValue); - double GetPrefNumber(ByteString prop, double defaultValue); - int GetPrefInteger(ByteString prop, int defaultValue); - unsigned int GetPrefUInteger(ByteString prop, unsigned int defaultValue); - bool GetPrefBool(ByteString prop, bool defaultValue); - std::vector GetPrefByteStringArray(ByteString prop); - std::vector GetPrefStringArray(ByteString prop); - std::vector GetPrefNumberArray(ByteString prop); - std::vector GetPrefIntegerArray(ByteString prop); - std::vector GetPrefUIntegerArray(ByteString prop); - std::vector GetPrefBoolArray(ByteString prop); - - void SetPref(ByteString prop, Json::Value value); - void SetPref(ByteString property, std::vector value); - void SetPrefUnicode(ByteString prop, String value); + + String DoMigration(ByteString fromDir, ByteString toDir); }; -#endif // CLIENT_H +bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor); diff --git a/src/client/ClientListener.h b/src/client/ClientListener.h index 09d3494b8..4a0cb84fa 100644 --- a/src/client/ClientListener.h +++ b/src/client/ClientListener.h @@ -1,7 +1,6 @@ -#ifndef CLIENTLISTENER_H_ -#define CLIENTLISTENER_H_ - +#pragma once #include "common/String.h" +#include "client/ServerNotification.h" class Client; class ClientListener @@ -13,8 +12,6 @@ public: virtual void NotifyUpdateAvailable(Client * sender) {} virtual void NotifyAuthUserChanged(Client * sender) {} virtual void NotifyMessageOfTheDay(Client * sender) {} - virtual void NotifyNewNotification(Client * sender, std::pair notification) {} + virtual void NotifyNewNotification(Client * sender, ServerNotification notification) {} }; - -#endif /* CLIENTLISTENER_H_ */ diff --git a/src/client/Comment.h b/src/client/Comment.h new file mode 100644 index 000000000..9978536ae --- /dev/null +++ b/src/client/Comment.h @@ -0,0 +1,11 @@ +#pragma once +#include "User.h" + +struct Comment +{ + ByteString authorName; + User::Elevation authorElevation; + bool authorIsSelf; + bool authorIsBanned; + String content; +}; diff --git a/src/client/GameSave.cpp b/src/client/GameSave.cpp index 1dfa00c2a..5d4533061 100644 --- a/src/client/GameSave.cpp +++ b/src/client/GameSave.cpp @@ -1,5 +1,13 @@ #include "GameSave.h" - +#include "bzip2/bz2wrap.h" +#include "Format.h" +#include "simulation/Simulation.h" +#include "simulation/ElementClasses.h" +#include "common/tpt-minmax.h" +#include "common/tpt-compat.h" +#include "bson/BSON.h" +#include "graphics/Renderer.h" +#include "Config.h" #include #include #include @@ -7,34 +15,22 @@ #include #include -#include "bzip2/bz2wrap.h" -#include "Config.h" -#include "Format.h" - -#include "simulation/Simulation.h" -#include "simulation/ElementClasses.h" - -#include "common/tpt-minmax.h" -#include "common/tpt-compat.h" -#include "bson/BSON.h" -#include "graphics/Renderer.h" - static void ConvertJsonToBson(bson *b, Json::Value j, int depth = 0); static void ConvertBsonToJson(bson_iterator *b, Json::Value *j, int depth = 0); static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen); static void CheckBsonFieldBool(bson_iterator iter, const char *field, bool *flag); static void CheckBsonFieldInt(bson_iterator iter, const char *field, int *setting); +static void CheckBsonFieldLong(bson_iterator iter, const char *field, int64_t *setting); static void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting); -GameSave::GameSave(int width, int height) +GameSave::GameSave(Vec2 newBlockSize) { - setSize(width, height); + setSize(newBlockSize); } -GameSave::GameSave(const std::vector &data) +GameSave::GameSave(const std::vector &data, bool newWantAuthors) { - blockWidth = 0; - blockHeight = 0; + wantAuthors = newWantAuthors; try { @@ -80,21 +76,22 @@ void GameSave::Expand(const std::vector &data) } } -void GameSave::setSize(int newWidth, int newHeight) +void GameSave::setSize(Vec2 newBlockSize) { - blockWidth = newWidth; - blockHeight = newHeight; + blockSize = newBlockSize; particlesCount = 0; particles = std::vector(NPART); - blockMap = Plane(blockWidth, blockHeight, 0); - fanVelX = Plane(blockWidth, blockHeight, 0.0f); - fanVelY = Plane(blockWidth, blockHeight, 0.0f); - pressure = Plane(blockWidth, blockHeight, 0.0f); - velocityX = Plane(blockWidth, blockHeight, 0.0f); - velocityY = Plane(blockWidth, blockHeight, 0.0f); - ambientHeat = Plane(blockWidth, blockHeight, 0.0f); + blockMap = PlaneAdapter>(blockSize, 0); + fanVelX = PlaneAdapter>(blockSize, 0.0f); + fanVelY = PlaneAdapter>(blockSize, 0.0f); + pressure = PlaneAdapter>(blockSize, 0.0f); + velocityX = PlaneAdapter>(blockSize, 0.0f); + velocityY = PlaneAdapter>(blockSize, 0.0f); + ambientHeat = PlaneAdapter>(blockSize, 0.0f); + blockAir = PlaneAdapter>(blockSize, 0); + blockAirh = PlaneAdapter>(blockSize, 0); } std::pair> GameSave::Serialise() const @@ -114,232 +111,141 @@ std::pair> GameSave::Serialise() const return { false, {} }; } -vector2d GameSave::Translate(vector2d translate) +extern const std::array, 8> Element_PIPE_offsets; +void Element_PIPE_transformPatchOffsets(Particle &part, const std::array &offsetMap); + +void GameSave::Transform(Mat2 transform, Vec2 nudge) { - float nx, ny; - vector2d pos; - vector2d translateReal = translate; - float minx = 0, miny = 0, maxx = 0, maxy = 0; - // determine minimum and maximum position of all particles / signs - for (size_t i = 0; i < signs.size(); i++) + // undo translation by rotation + auto br = transform * (blockSize * CELL - Vec2{ 1, 1 }); + auto bbr = transform * (blockSize - Vec2{ 1, 1 }); + auto translate = Vec2{ std::max(0, -br.X), std::max(0, -br.Y) }; + auto btranslate = Vec2{ std::max(0, -bbr.X), std::max(0, -bbr.Y) }; + auto newBlockS = transform * blockSize; + newBlockS.X = std::abs(newBlockS.X); + newBlockS.Y = std::abs(newBlockS.Y); + translate += nudge; + + // Grow as needed. + assert((Vec2{ CELL, CELL }.OriginRect().Contains(nudge))); + if (nudge.X) newBlockS.X += 1; + if (nudge.Y) newBlockS.Y += 1; + + // TODO: allow transforms to yield bigger saves. For this we'd need SaveRenderer (the singleton, not Renderer) + // to fully render them (possible with stitching) and Simulation::Load to be able to take only the part that fits. + newBlockS = newBlockS.Clamp(RectBetween({ 0, 0 }, CELLS)); + auto newPartS = newBlockS * CELL; + + // Prepare to patch pipes. + std::array pipeOffsetMap; { - pos = v2d_new(float(signs[i].x), float(signs[i].y)); - pos = v2d_add(pos,translate); - nx = floor(pos.x+0.5f); - ny = floor(pos.y+0.5f); - if (nx < minx) - minx = nx; - if (ny < miny) - miny = ny; - if (nx > maxx) - maxx = nx; - if (ny > maxy) - maxy = ny; + std::transform(Element_PIPE_offsets.begin(), Element_PIPE_offsets.end(), pipeOffsetMap.begin(), [transform](auto offset) { + auto it = std::find(Element_PIPE_offsets.begin(), Element_PIPE_offsets.end(), transform * offset); + assert(it != Element_PIPE_offsets.end()); + return int(it - Element_PIPE_offsets.begin()); + }); } - for (int i = 0; i < particlesCount; i++) + + // Translate signs. + for (auto i = 0U; i < signs.size(); i++) { - if (!particles[i].type) continue; - pos = v2d_new(particles[i].x, particles[i].y); - pos = v2d_add(pos,translate); - nx = floor(pos.x+0.5f); - ny = floor(pos.y+0.5f); - if (nx < minx) - minx = nx; - if (ny < miny) - miny = ny; - if (nx > maxx) - maxx = nx; - if (ny > maxy) - maxy = ny; - } - // determine whether corrections are needed. If moving in this direction would delete stuff, expand the save - vector2d backCorrection = v2d_new( - (minx < 0) ? (-floor(minx / CELL)) : 0, - (miny < 0) ? (-floor(miny / CELL)) : 0 - ); - int blockBoundsX = int(maxx / CELL) + 1, blockBoundsY = int(maxy / CELL) + 1; - vector2d frontCorrection = v2d_new( - float((blockBoundsX > blockWidth) ? (blockBoundsX - blockWidth) : 0), - float((blockBoundsY > blockHeight) ? (blockBoundsY - blockHeight) : 0) - ); - - // get new width based on corrections - auto newWidth = int((blockWidth + backCorrection.x + frontCorrection.x) * CELL); - auto newHeight = int((blockHeight + backCorrection.y + frontCorrection.y) * CELL); - if (newWidth > XRES) - frontCorrection.x = backCorrection.x = 0; - if (newHeight > YRES) - frontCorrection.y = backCorrection.y = 0; - - // call Transform to do the transformation we wanted when calling this function - translate = v2d_add(translate, v2d_multiply_float(backCorrection, CELL)); - Transform(m2d_identity, translate, translateReal, - int((blockWidth + backCorrection.x + frontCorrection.x) * CELL), - int((blockHeight + backCorrection.y + frontCorrection.y) * CELL) - ); - - // return how much we corrected. This is used to offset the position of the current stamp - // otherwise it would attempt to recenter it with the current size - return v2d_add(v2d_multiply_float(backCorrection, -CELL), v2d_multiply_float(frontCorrection, CELL)); -} - -void GameSave::Transform(matrix2d transform, vector2d translate) -{ - int width = blockWidth*CELL, height = blockHeight*CELL, newWidth, newHeight; - vector2d tmp, ctl, cbr; - vector2d cornerso[4]; - vector2d translateReal = translate; - // undo any translation caused by rotation - cornerso[0] = v2d_new(0,0); - cornerso[1] = v2d_new(float(width-1),0); - cornerso[2] = v2d_new(0,float(height-1)); - cornerso[3] = v2d_new(float(width-1),float(height-1)); - for (int i = 0; i < 4; i++) - { - tmp = m2d_multiply_v2d(transform,cornerso[i]); - if (i==0) ctl = cbr = tmp; // top left, bottom right corner - if (tmp.xcbr.x) cbr.x = tmp.x; - if (tmp.y>cbr.y) cbr.y = tmp.y; - } - // casting as int doesn't quite do what we want with negative numbers, so use floor() - tmp = v2d_new(floor(ctl.x+0.5f),floor(ctl.y+0.5f)); - translate = v2d_sub(translate,tmp); - newWidth = int(floor(cbr.x+0.5f))-int(floor(ctl.x+0.5f))+1; - newHeight = int(floor(cbr.y+0.5f))-int(floor(ctl.y+0.5f))+1; - Transform(transform, translate, translateReal, newWidth, newHeight); -} - -// transform is a matrix describing how we want to rotate this save -// translate can vary depending on whether the save is bring rotated, or if a normal translate caused it to expand -// translateReal is the original amount we tried to translate, used to calculate wall shifting -void GameSave::Transform(matrix2d transform, vector2d translate, vector2d translateReal, int newWidth, int newHeight) -{ - if (newWidth>XRES) newWidth = XRES; - if (newHeight>YRES) newHeight = YRES; - - int x, y, nx, ny, newBlockWidth = newWidth / CELL, newBlockHeight = newHeight / CELL; - vector2d pos, vel; - - Plane blockMapNew(newBlockWidth, newBlockHeight, 0); - Plane fanVelXNew(newBlockWidth, newBlockHeight, 0.0f); - Plane fanVelYNew(newBlockWidth, newBlockHeight, 0.0f); - Plane pressureNew(newBlockWidth, newBlockHeight, 0.0f); - Plane velocityXNew(newBlockWidth, newBlockHeight, 0.0f); - Plane velocityYNew(newBlockWidth, newBlockHeight, 0.0f); - Plane ambientHeatNew(newBlockWidth, newBlockHeight, 0.0f); - - // Match these up with the matrices provided in GameView::OnKeyPress. - bool patchPipeR = transform.a == 0 && transform.b == 1 && transform.c == -1 && transform.d == 0; - bool patchPipeH = transform.a == -1 && transform.b == 0 && transform.c == 0 && transform.d == 1; - bool patchPipeV = transform.a == 1 && transform.b == 0 && transform.c == 0 && transform.d == -1; - - // rotate and translate signs, parts, walls - for (size_t i = 0; i < signs.size(); i++) - { - pos = v2d_new(float(signs[i].x), float(signs[i].y)); - pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); - nx = int(floor(pos.x+0.5f)); - ny = int(floor(pos.y+0.5f)); - if (nx<0 || nx>=newWidth || ny<0 || ny>=newHeight) + auto newPos = transform * Vec2{ signs[i].x, signs[i].y } + translate; + if (!newPartS.OriginRect().Contains(newPos)) { - signs[i].text[0] = 0; + signs[i].text.clear(); continue; } - signs[i].x = nx; - signs[i].y = ny; + signs[i].x = newPos.X; + signs[i].y = newPos.Y; } + + // Translate particles. for (int i = 0; i < particlesCount; i++) { - if (!particles[i].type) continue; - pos = v2d_new(particles[i].x, particles[i].y); - pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); - nx = int(floor(pos.x+0.5f)); - ny = int(floor(pos.y+0.5f)); - if (nx<0 || nx>=newWidth || ny<0 || ny>=newHeight) + if (!particles[i].type) + { + continue; + } + { + // * We want particles to retain their distance from the centre of the particle grid cell + // they are in, but more importantly we also don't want them to change grid cells, + // so we just get rid of the edge cases. + constexpr auto threshold = 0.001f; + auto boundaryDiffX = particles[i].x - (floor(particles[i].x) + 0.5f); + if (fabs(boundaryDiffX) < threshold) + { + particles[i].x += copysign(threshold, boundaryDiffX); + } + auto boundaryDiffY = particles[i].y - (floor(particles[i].y) + 0.5f); + if (fabs(boundaryDiffY) < threshold) + { + particles[i].y += copysign(threshold, boundaryDiffY); + } + } + if (particles[i].x - floor(particles[i].x) == 0.5f) particles[i].x += 0.001f; + if (particles[i].y - floor(particles[i].y) == 0.5f) particles[i].y += 0.001f; + auto newPos = transform * Vec2{ particles[i].x, particles[i].y } + translate; + if (!newPartS.OriginRect().Contains(Vec2{ int(floor(newPos.X + 0.5f)), int(floor(newPos.Y + 0.5f)) })) { particles[i].type = PT_NONE; continue; } - particles[i].x = float(nx); - particles[i].y = float(ny); - vel = v2d_new(particles[i].vx, particles[i].vy); - vel = m2d_multiply_v2d(transform, vel); - particles[i].vx = vel.x; - particles[i].vy = vel.y; + particles[i].x = newPos.X; + particles[i].y = newPos.Y; + auto newVel = transform * Vec2{ particles[i].vx, particles[i].vy }; + particles[i].vx = newVel.X; + particles[i].vy = newVel.Y; if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP) { - if (patchPipeR) - { - void Element_PIPE_patchR(Particle &part); - Element_PIPE_patchR(particles[i]); - } - if (patchPipeH) - { - void Element_PIPE_patchH(Particle &part); - Element_PIPE_patchH(particles[i]); - } - if (patchPipeV) - { - void Element_PIPE_patchV(Particle &part); - Element_PIPE_patchV(particles[i]); - } + Element_PIPE_transformPatchOffsets(particles[i], pipeOffsetMap); } } - // translate walls and other grid items when the stamp is shifted more than 4 pixels in any direction - int translateX = 0, translateY = 0; - if (translateReal.x > 0 && ((int)translated.x%CELL == 3 - || (translated.x < 0 && (int)translated.x%CELL == 0))) - translateX = CELL; - else if (translateReal.x < 0 && ((int)translated.x%CELL == -3 - || (translated.x > 0 && (int)translated.x%CELL == 0))) - translateX = -CELL; - if (translateReal.y > 0 && ((int)translated.y%CELL == 3 - || (translated.y < 0 && (int)translated.y%CELL == 0))) - translateY = CELL; - else if (translateReal.y < 0 && ((int)translated.y%CELL == -3 - || (translated.y > 0 && (int)translated.y%CELL == 0))) - translateY = -CELL; - - for (y=0; y> newBlockMap(newBlockS, 0); + PlaneAdapter> newFanVelX(newBlockS, 0.0f); + PlaneAdapter> newFanVelY(newBlockS, 0.0f); + PlaneAdapter> newPressure(newBlockS, 0.0f); + PlaneAdapter> newVelocityX(newBlockS, 0.0f); + PlaneAdapter> newVelocityY(newBlockS, 0.0f); + PlaneAdapter> newAmbientHeat(newBlockS, 0.0f); + PlaneAdapter> newBlockAir(newBlockS, 0); + PlaneAdapter> newBlockAirh(newBlockS, 0); + for (auto bpos : blockSize.OriginRect()) + { + auto newBpos = transform * bpos + btranslate; + if (!newBlockS.OriginRect().Contains(newBpos)) { - pos = v2d_new(x*CELL+CELL*0.4f+translateX, y*CELL+CELL*0.4f+translateY); - pos = v2d_add(m2d_multiply_v2d(transform,pos),translate); - nx = int(pos.x/CELL); - ny = int(pos.y/CELL); - if (pos.x<0 || nx>=newBlockWidth || pos.y<0 || ny>=newBlockHeight) - continue; - if (blockMap[y][x]) - { - blockMapNew[ny][nx] = blockMap[y][x]; - if (blockMap[y][x]==WL_FAN) - { - vel = v2d_new(fanVelX[y][x], fanVelY[y][x]); - vel = m2d_multiply_v2d(transform, vel); - fanVelXNew[ny][nx] = vel.x; - fanVelYNew[ny][nx] = vel.y; - } - } - pressureNew[ny][nx] = pressure[y][x]; - velocityXNew[ny][nx] = velocityX[y][x]; - velocityYNew[ny][nx] = velocityY[y][x]; - ambientHeatNew[ny][nx] = ambientHeat[y][x]; + continue; } - translated = v2d_add(m2d_multiply_v2d(transform, translated), translateReal); + if (blockMap[bpos]) + { + newBlockMap[newBpos] = blockMap[bpos]; + if (blockMap[bpos] == WL_FAN) + { + auto newVel = transform * Vec2{ fanVelX[bpos], fanVelY[bpos] }; + newFanVelX[newBpos] = newVel.X; + newFanVelY[newBpos] = newVel.Y; + } + } + newPressure[newBpos] = pressure[bpos]; + newVelocityX[newBpos] = velocityX[bpos]; + newVelocityY[newBpos] = velocityY[bpos]; + newAmbientHeat[newBpos] = ambientHeat[bpos]; + newBlockAir[newBpos] = blockAir[bpos]; + newBlockAirh[newBpos] = blockAirh[bpos]; + } + blockMap = std::move(newBlockMap); + fanVelX = std::move(newFanVelX); + fanVelY = std::move(newFanVelY); + pressure = std::move(newPressure); + velocityX = std::move(newVelocityX); + velocityY = std::move(newVelocityY); + ambientHeat = std::move(newAmbientHeat); + blockAir = std::move(newBlockAir); + blockAirh = std::move(newBlockAirh); - blockWidth = newBlockWidth; - blockHeight = newBlockHeight; - - blockMap = blockMapNew; - fanVelX = fanVelXNew; - fanVelY = fanVelYNew; - pressure = pressureNew; - velocityX = velocityXNew; - velocityY = velocityYNew; - ambientHeat = ambientHeatNew; + blockSize = newBlockS; } static void CheckBsonFieldUser(bson_iterator iter, const char *field, unsigned char **data, unsigned int *fieldLen) @@ -387,6 +293,21 @@ static void CheckBsonFieldInt(bson_iterator iter, const char *field, int *settin } } +static void CheckBsonFieldLong(bson_iterator iter, const char *field, int64_t *setting) +{ + if (!strcmp(bson_iterator_key(&iter), field)) + { + if (bson_iterator_type(&iter) == BSON_LONG) + { + *setting = bson_iterator_long(&iter); + } + else + { + fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter)); + } + } +} + static void CheckBsonFieldFloat(bson_iterator iter, const char *field, float *setting) { if (!strcmp(bson_iterator_key(&iter), field)) @@ -407,11 +328,10 @@ void GameSave::readOPS(const std::vector &data) Renderer::PopulateTables(); unsigned char *inputData = (unsigned char*)&data[0], *partsData = NULL, *partsPosData = NULL, *fanData = NULL, *wallData = NULL, *soapLinkData = NULL; - unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL; + unsigned char *pressData = NULL, *vxData = NULL, *vyData = NULL, *ambientData = NULL, *blockAirData = nullptr; unsigned int inputDataLen = data.size(), bsonDataLen = 0, partsDataLen, partsPosDataLen, fanDataLen, wallDataLen, soapLinkDataLen; - unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen; + unsigned int pressDataLen, vxDataLen, vyDataLen, ambientDataLen, blockAirDataLen; unsigned partsCount = 0; - unsigned int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH; int savedVersion = inputData[4]; majorVersion = savedVersion; minorVersion = 0; @@ -425,16 +345,12 @@ void GameSave::readOPS(const std::vector &data) std::unique_ptr b_ptr(&b, bson_deleter); //Block sizes - blockX = 0; - blockY = 0; - blockW = inputData[6]; - blockH = inputData[7]; + auto blockP = Vec2{ 0, 0 }; + auto blockS = Vec2{ int(inputData[6]), int(inputData[7]) }; //Full size, normalised - fullX = blockX*CELL; - fullY = blockY*CELL; - fullW = blockW*CELL; - fullH = blockH*CELL; + auto partP = blockP * CELL; + auto partS = blockS * CELL; //From newer version if (savedVersion > SAVE_VERSION) @@ -447,14 +363,14 @@ void GameSave::readOPS(const std::vector &data) if (inputData[5] != CELL) throw ParseException(ParseException::InvalidDimensions, "Incorrect CELL size"); - if (blockW <= 0 || blockH <= 0) - throw ParseException(ParseException::InvalidDimensions, "Save too small"); + if (!RectBetween({ 0, 0 }, CELLS).Contains(blockS)) + throw ParseException(ParseException::InvalidDimensions, "Save is of invalid size"); //Too large/off screen - if (blockX+blockW > XRES/CELL || blockY+blockH > YRES/CELL) - throw ParseException(ParseException::InvalidDimensions, "Save too large"); + if (!RectBetween({ 0, 0 }, CELLS).Contains(blockP + blockS)) + throw ParseException(ParseException::InvalidDimensions, "Save extends beyond canvas"); - setSize(blockW, blockH); + setSize(blockS); bsonDataLen = ((unsigned)inputData[8]); bsonDataLen |= ((unsigned)inputData[9]) << 8; @@ -501,6 +417,7 @@ void GameSave::readOPS(const std::vector &data) CheckBsonFieldUser(iter, "vxMap", &vxData, &vxDataLen); CheckBsonFieldUser(iter, "vyMap", &vyData, &vyDataLen); CheckBsonFieldUser(iter, "ambientMap", &ambientData, &ambientDataLen); + CheckBsonFieldUser(iter, "blockAir", &blockAirData, &blockAirDataLen); CheckBsonFieldUser(iter, "fanMap", &fanData, &fanDataLen); CheckBsonFieldUser(iter, "soapLinks", &soapLinkData, &soapLinkDataLen); CheckBsonFieldBool(iter, "legacyEnable", &legacyEnable); @@ -515,7 +432,23 @@ void GameSave::readOPS(const std::vector &data) CheckBsonFieldFloat(iter, "ambientAirTemp", &ambientAirTemp); CheckBsonFieldInt(iter, "edgeMode", &edgeMode); CheckBsonFieldInt(iter, "pmapbits", &pmapbits); - if (!strcmp(bson_iterator_key(&iter), "signs")) + CheckBsonFieldBool(iter, "ensureDeterminism", &ensureDeterminism); + CheckBsonFieldLong(iter, "frameCount", reinterpret_cast(&frameCount)); + CheckBsonFieldLong(iter, "rngState0", reinterpret_cast(&rngState[0])); + CheckBsonFieldLong(iter, "rngState1", reinterpret_cast(&rngState[1])); + if (!strcmp(bson_iterator_key(&iter), "rngState")) + { + if (bson_iterator_type(&iter) == BSON_BINDATA && ((unsigned char)bson_iterator_bin_type(&iter)) == BSON_BIN_USER && bson_iterator_bin_len(&iter) == sizeof(rngState)) + { + memcpy(&rngState, bson_iterator_bin_data(&iter), sizeof(rngState)); + hasRngState = true; + } + else + { + fprintf(stderr, "Invalid datatype for rngState: %d[%d] %d[%d] %d[%d]\n", bson_iterator_type(&iter), bson_iterator_type(&iter)==BSON_BINDATA, (unsigned char)bson_iterator_bin_type(&iter), ((unsigned char)bson_iterator_bin_type(&iter))==BSON_BIN_USER, bson_iterator_bin_len(&iter), bson_iterator_bin_len(&iter)>0); + } + } + else if (!strcmp(bson_iterator_key(&iter), "signs")) { if (bson_iterator_type(&iter)==BSON_ARRAY) { @@ -554,11 +487,11 @@ void GameSave::readOPS(const std::vector &data) } else if (!strcmp(bson_iterator_key(&signiter), "x") && bson_iterator_type(&signiter) == BSON_INT) { - tempSign.x = bson_iterator_int(&signiter)+fullX; + tempSign.x = bson_iterator_int(&signiter)+partP.X; } else if (!strcmp(bson_iterator_key(&signiter), "y") && bson_iterator_type(&signiter) == BSON_INT) { - tempSign.y = bson_iterator_int(&signiter)+fullY; + tempSign.y = bson_iterator_int(&signiter)+partP.Y; } else { @@ -675,27 +608,24 @@ void GameSave::readOPS(const std::vector &data) minor = bson_iterator_int(&subiter); } } -#if defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0 - if (major > FUTURE_SAVE_VERSION || (major == FUTURE_SAVE_VERSION && minor > FUTURE_MINOR_VERSION)) -#else - if (major > SAVE_VERSION || (major == SAVE_VERSION && minor > MINOR_VERSION)) -#endif + auto majorToCheck = ALLOW_FAKE_NEWER_VERSION ? FUTURE_SAVE_VERSION : SAVE_VERSION; + auto minorToCheck = ALLOW_FAKE_NEWER_VERSION ? FUTURE_MINOR_VERSION : MINOR_VERSION; + if (major > majorToCheck || (major == majorToCheck && minor > minorToCheck)) { String errorMessage = String::Build("Save from a newer version: Requires version ", major, ".", minor); throw ParseException(ParseException::WrongVersion, errorMessage); } -#if defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0 - else if (major > SAVE_VERSION || (major == SAVE_VERSION && minor > MINOR_VERSION)) + else if (ALLOW_FAKE_NEWER_VERSION && (major > SAVE_VERSION || (major == SAVE_VERSION && minor > MINOR_VERSION))) + { fakeNewerVersion = true; -#endif + } } else { fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter)); } } -#ifndef RENDERER - else if (!strcmp(bson_iterator_key(&iter), "authors")) + else if (wantAuthors && !strcmp(bson_iterator_key(&iter), "authors")) { if (bson_iterator_type(&iter) == BSON_OBJECT) { @@ -709,68 +639,56 @@ void GameSave::readOPS(const std::vector &data) fprintf(stderr, "Wrong type for %s\n", bson_iterator_key(&iter)); } } -#endif } //Read wall and fan data if(wallData) { + // TODO: use PlaneAdapter> once we're C++20 + auto wallDataPlane = PlaneAdapter>(blockS, std::in_place, wallData, blockS.X * blockS.Y); unsigned int j = 0; - if (blockW * blockH > wallDataLen) + if (blockS.X * blockS.Y > int(wallDataLen)) throw ParseException(ParseException::Corrupt, "Not enough wall data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) + unsigned char bm = 0; + if (wallDataPlane[bpos]) + bm = wallDataPlane[bpos]; + + switch (bm) { - if (wallData[y*blockW+x]) - blockMap[blockY+y][blockX+x] = wallData[y*blockW+x]; - - if (blockMap[y][x]==O_WL_WALLELEC) - blockMap[y][x]=WL_WALLELEC; - if (blockMap[y][x]==O_WL_EWALL) - blockMap[y][x]=WL_EWALL; - if (blockMap[y][x]==O_WL_DETECT) - blockMap[y][x]=WL_DETECT; - if (blockMap[y][x]==O_WL_STREAM) - blockMap[y][x]=WL_STREAM; - if (blockMap[y][x]==O_WL_FAN||blockMap[y][x]==O_WL_FANHELPER) - blockMap[y][x]=WL_FAN; - if (blockMap[y][x]==O_WL_ALLOWLIQUID) - blockMap[y][x]=WL_ALLOWLIQUID; - if (blockMap[y][x]==O_WL_DESTROYALL) - blockMap[y][x]=WL_DESTROYALL; - if (blockMap[y][x]==O_WL_ERASE) - blockMap[y][x]=WL_ERASE; - if (blockMap[y][x]==O_WL_WALL) - blockMap[y][x]=WL_WALL; - if (blockMap[y][x]==O_WL_ALLOWAIR) - blockMap[y][x]=WL_ALLOWAIR; - if (blockMap[y][x]==O_WL_ALLOWSOLID) - blockMap[y][x]=WL_ALLOWPOWDER; - if (blockMap[y][x]==O_WL_ALLOWALLELEC) - blockMap[y][x]=WL_ALLOWALLELEC; - if (blockMap[y][x]==O_WL_EHOLE) - blockMap[y][x]=WL_EHOLE; - if (blockMap[y][x]==O_WL_ALLOWGAS) - blockMap[y][x]=WL_ALLOWGAS; - if (blockMap[y][x]==O_WL_GRAV) - blockMap[y][x]=WL_GRAV; - if (blockMap[y][x]==O_WL_ALLOWENERGY) - blockMap[y][x]=WL_ALLOWENERGY; - - if (blockMap[y][x] == WL_FAN && fanData) - { - if(j+1 >= fanDataLen) - { - fprintf(stderr, "Not enough fan data\n"); - } - fanVelX[blockY+y][blockX+x] = (fanData[j++]-127.0f)/64.0f; - fanVelY[blockY+y][blockX+x] = (fanData[j++]-127.0f)/64.0f; - } - - if (blockMap[y][x] >= UI_WALLCOUNT) - blockMap[y][x] = 0; + case O_WL_WALLELEC: bm = WL_WALLELEC; break; + case O_WL_EWALL: bm = WL_EWALL; break; + case O_WL_DETECT: bm = WL_DETECT; break; + case O_WL_STREAM: bm = WL_STREAM; break; + case O_WL_FAN: + case O_WL_FANHELPER: bm = WL_FAN; break; + case O_WL_ALLOWLIQUID: bm = WL_ALLOWLIQUID; break; + case O_WL_DESTROYALL: bm = WL_DESTROYALL; break; + case O_WL_ERASE: bm = WL_ERASE; break; + case O_WL_WALL: bm = WL_WALL; break; + case O_WL_ALLOWAIR: bm = WL_ALLOWAIR; break; + case O_WL_ALLOWSOLID: bm = WL_ALLOWPOWDER; break; + case O_WL_ALLOWALLELEC: bm = WL_ALLOWALLELEC; break; + case O_WL_EHOLE: bm = WL_EHOLE; break; + case O_WL_ALLOWGAS: bm = WL_ALLOWGAS; break; + case O_WL_GRAV: bm = WL_GRAV; break; + case O_WL_ALLOWENERGY: bm = WL_ALLOWENERGY; break; } + + if (bm == WL_FAN && fanData) + { + if(j+1 >= fanDataLen) + { + fprintf(stderr, "Not enough fan data\n"); + } + fanVelX[blockP + bpos] = (fanData[j++]-127.0f)/64.0f; + fanVelY[blockP + bpos] = (fanData[j++]-127.0f)/64.0f; + } + + if (bm >= UI_WALLCOUNT) + bm = 0; + blockMap[blockP + bpos] = bm; } } @@ -779,16 +697,13 @@ void GameSave::readOPS(const std::vector &data) { unsigned int j = 0; unsigned char i, i2; - if (blockW * blockH > pressDataLen) + if (blockS.X * blockS.Y > int(pressDataLen)) throw ParseException(ParseException::Corrupt, "Not enough pressure data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - i = pressData[j++]; - i2 = pressData[j++]; - pressure[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256; - } + i = pressData[j++]; + i2 = pressData[j++]; + pressure[blockP + bpos] = ((i+(i2<<8))/128.0f)-256; } hasPressure = true; } @@ -798,16 +713,13 @@ void GameSave::readOPS(const std::vector &data) { unsigned int j = 0; unsigned char i, i2; - if (blockW * blockH > vxDataLen) + if (blockS.X * blockS.Y > int(vxDataLen)) throw ParseException(ParseException::Corrupt, "Not enough vx data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - i = vxData[j++]; - i2 = vxData[j++]; - velocityX[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256; - } + i = vxData[j++]; + i2 = vxData[j++]; + velocityX[blockP + bpos] = ((i+(i2<<8))/128.0f)-256; } } @@ -816,16 +728,13 @@ void GameSave::readOPS(const std::vector &data) { unsigned int j = 0; unsigned char i, i2; - if (blockW * blockH > vyDataLen) + if (blockS.X * blockS.Y > int(vyDataLen)) throw ParseException(ParseException::Corrupt, "Not enough vy data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - i = vyData[j++]; - i2 = vyData[j++]; - velocityY[blockY+y][blockX+x] = ((i+(i2<<8))/128.0f)-256; - } + i = vyData[j++]; + i2 = vyData[j++]; + velocityY[blockP + bpos] = ((i+(i2<<8))/128.0f)-256; } } @@ -833,369 +742,373 @@ void GameSave::readOPS(const std::vector &data) if (ambientData) { unsigned int i = 0, tempTemp; - if (blockW * blockH > ambientDataLen) + if (blockS.X * blockS.Y > int(ambientDataLen)) throw ParseException(ParseException::Corrupt, "Not enough ambient heat data"); - for (unsigned int x = 0; x < blockW; x++) + for (auto bpos : blockS.OriginRect().Range()) { - for (unsigned int y = 0; y < blockH; y++) - { - tempTemp = ambientData[i++]; - tempTemp |= (((unsigned)ambientData[i++]) << 8); - ambientHeat[blockY+y][blockX+x] = float(tempTemp); - } + tempTemp = ambientData[i++]; + tempTemp |= (((unsigned)ambientData[i++]) << 8); + ambientHeat[blockP + bpos] = float(tempTemp); } hasAmbientHeat = true; } + if (blockAirData) + { + if (blockS.X * blockS.Y * 2 > int(blockAirDataLen)) + throw ParseException(ParseException::Corrupt, "Not enough block air data"); + // TODO: use PlaneAdapter> once we're C++20 + auto blockAirDataPlane = PlaneAdapter>(blockS, std::in_place, blockAirData, blockS.X * blockS.Y); + auto blockAirhDataPlane = PlaneAdapter>(blockS, std::in_place, blockAirData + blockS.X * blockS.Y, blockS.X * blockS.Y); + for (auto bpos : blockS.OriginRect().Range()) + { + blockAir[blockP + bpos] = blockAirDataPlane[bpos]; + blockAirh[blockP + bpos] = blockAirhDataPlane[bpos]; + } + hasBlockAirMaps = true; + } + //Read particle data if (partsData && partsPosData) { int newIndex = 0, tempTemp; int posCount, posTotal, partsPosDataIndex = 0; - if (fullW * fullH * 3 > partsPosDataLen) + if (partS.X * partS.Y * 3 > int(partsPosDataLen)) throw ParseException(ParseException::Corrupt, "Not enough particle position data"); partsCount = 0; unsigned int i = 0; - unsigned int saved_x, saved_y, x, y; newIndex = 0; - for (saved_y = 0; saved_y < fullH; saved_y++) + for (auto pos : RectSized(partP, partS).Range()) { - for (saved_x = 0; saved_x < fullW; saved_x++) + //Read total number of particles at this position + posTotal = 0; + posTotal |= partsPosData[partsPosDataIndex++]<<16; + posTotal |= partsPosData[partsPosDataIndex++]<<8; + posTotal |= partsPosData[partsPosDataIndex++]; + //Put the next posTotal particles at this position + for (posCount = 0; posCount < posTotal; posCount++) { - //Read total number of particles at this position - posTotal = 0; - posTotal |= partsPosData[partsPosDataIndex++]<<16; - posTotal |= partsPosData[partsPosDataIndex++]<<8; - posTotal |= partsPosData[partsPosDataIndex++]; - //Put the next posTotal particles at this position - for (posCount = 0; posCount < posTotal; posCount++) + particlesCount = newIndex+1; + //i+3 because we have 4 bytes of required fields (type (1), descriptor (2), temp (1)) + if (i+3 >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer"); + unsigned int fieldDescriptor = (unsigned int)(partsData[i+1]); + fieldDescriptor |= (unsigned int)(partsData[i+2]) << 8; + + if (newIndex < 0 || newIndex >= NPART) + throw ParseException(ParseException::Corrupt, "Too many particles"); + + //Clear the particle, ready for our new properties + memset(&(particles[newIndex]), 0, sizeof(Particle)); + + //Required fields + particles[newIndex].type = partsData[i]; + particles[newIndex].x = float(pos.X); + particles[newIndex].y = float(pos.Y); + i+=3; + + // Read type (2nd byte) + if (fieldDescriptor & 0x4000) + particles[newIndex].type |= (((unsigned)partsData[i++]) << 8); + + //Read temp + if(fieldDescriptor & 0x01) { - particlesCount = newIndex+1; - //i+3 because we have 4 bytes of required fields (type (1), descriptor (2), temp (1)) - if (i+3 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer"); - x = saved_x + fullX; - y = saved_y + fullY; - unsigned int fieldDescriptor = (unsigned int)(partsData[i+1]); - fieldDescriptor |= (unsigned int)(partsData[i+2]) << 8; - if (x >= fullW || y >= fullH) - throw ParseException(ParseException::Corrupt, "Particle out of range"); - - if (newIndex < 0 || newIndex >= NPART) - throw ParseException(ParseException::Corrupt, "Too many particles"); - - //Clear the particle, ready for our new properties - memset(&(particles[newIndex]), 0, sizeof(Particle)); - - //Required fields - particles[newIndex].type = partsData[i]; - particles[newIndex].x = float(x); - particles[newIndex].y = float(y); - i+=3; - - // Read type (2nd byte) - if (fieldDescriptor & 0x4000) - particles[newIndex].type |= (((unsigned)partsData[i++]) << 8); - - //Read temp - if(fieldDescriptor & 0x01) + //Full 16bit int + tempTemp = partsData[i++]; + tempTemp |= (((unsigned)partsData[i++]) << 8); + particles[newIndex].temp = float(tempTemp); + } + else + { + //1 Byte room temp offset + tempTemp = partsData[i++]; + if (tempTemp >= 0x80) { - //Full 16bit int - tempTemp = partsData[i++]; - tempTemp |= (((unsigned)partsData[i++]) << 8); - particles[newIndex].temp = float(tempTemp); - } - else - { - //1 Byte room temp offset - tempTemp = partsData[i++]; - if (tempTemp >= 0x80) - { - tempTemp -= 0x100; - } - particles[newIndex].temp = tempTemp+294.15f; + tempTemp -= 0x100; } + particles[newIndex].temp = tempTemp+294.15f; + } - // fieldDesc3 - if (fieldDescriptor & 0x8000) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading third byte of field descriptor"); - fieldDescriptor |= (unsigned int)(partsData[i++]) << 16; - } + // fieldDesc3 + if (fieldDescriptor & 0x8000) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading third byte of field descriptor"); + fieldDescriptor |= (unsigned int)(partsData[i++]) << 16; + } - //Read life - if(fieldDescriptor & 0x02) + //Read life + if(fieldDescriptor & 0x02) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading life"); + particles[newIndex].life = partsData[i++]; + //i++; + //Read 2nd byte + if(fieldDescriptor & 0x04) { if (i >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading life"); - particles[newIndex].life = partsData[i++]; - //i++; - //Read 2nd byte - if(fieldDescriptor & 0x04) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading life"); - particles[newIndex].life |= (((unsigned)partsData[i++]) << 8); - } + particles[newIndex].life |= (((unsigned)partsData[i++]) << 8); } + } - //Read tmp - if(fieldDescriptor & 0x08) + //Read tmp + if(fieldDescriptor & 0x08) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp"); + particles[newIndex].tmp = partsData[i++]; + //Read 2nd byte + if(fieldDescriptor & 0x10) { if (i >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp"); - particles[newIndex].tmp = partsData[i++]; - //Read 2nd byte - if(fieldDescriptor & 0x10) + particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 8); + //Read 3rd and 4th bytes + if(fieldDescriptor & 0x1000) { - if (i >= partsDataLen) + if (i+1 >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp"); - particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 8); - //Read 3rd and 4th bytes - if(fieldDescriptor & 0x1000) - { - if (i+1 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp"); - particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 24); - particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 16); - } + particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 24); + particles[newIndex].tmp |= (((unsigned)partsData[i++]) << 16); } } + } - //Read ctype - if(fieldDescriptor & 0x20) + //Read ctype + if(fieldDescriptor & 0x20) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading ctype"); + particles[newIndex].ctype = partsData[i++]; + //Read additional bytes + if(fieldDescriptor & 0x200) { - if (i >= partsDataLen) + if (i+2 >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading ctype"); - particles[newIndex].ctype = partsData[i++]; - //Read additional bytes - if(fieldDescriptor & 0x200) - { - if (i+2 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading ctype"); - particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 24); - particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 16); - particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 8); - } + particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 24); + particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 16); + particles[newIndex].ctype |= (((unsigned)partsData[i++]) << 8); } + } - //Read dcolour - if(fieldDescriptor & 0x40) - { - if (i+3 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading deco"); - particles[newIndex].dcolour = (((unsigned)partsData[i++]) << 24); - particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 16); - particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 8); - particles[newIndex].dcolour |= ((unsigned)partsData[i++]); - } + //Read dcolour + if(fieldDescriptor & 0x40) + { + if (i+3 >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading deco"); + particles[newIndex].dcolour = (((unsigned)partsData[i++]) << 24); + particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 16); + particles[newIndex].dcolour |= (((unsigned)partsData[i++]) << 8); + particles[newIndex].dcolour |= ((unsigned)partsData[i++]); + } - //Read vx - if(fieldDescriptor & 0x80) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vx"); - particles[newIndex].vx = (partsData[i++]-127.0f)/16.0f; - } + //Read vx + if(fieldDescriptor & 0x80) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vx"); + particles[newIndex].vx = (partsData[i++]-127.0f)/16.0f; + } - //Read vy - if(fieldDescriptor & 0x100) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vy"); - particles[newIndex].vy = (partsData[i++]-127.0f)/16.0f; - } + //Read vy + if(fieldDescriptor & 0x100) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading vy"); + particles[newIndex].vy = (partsData[i++]-127.0f)/16.0f; + } - //Read tmp2 - if(fieldDescriptor & 0x400) + //Read tmp2 + if(fieldDescriptor & 0x400) + { + if (i >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp2"); + particles[newIndex].tmp2 = partsData[i++]; + if(fieldDescriptor & 0x800) { if (i >= partsDataLen) throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp2"); - particles[newIndex].tmp2 = partsData[i++]; - if(fieldDescriptor & 0x800) - { - if (i >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp2"); - particles[newIndex].tmp2 |= (((unsigned)partsData[i++]) << 8); - } + particles[newIndex].tmp2 |= (((unsigned)partsData[i++]) << 8); } + } - //Read tmp3 and tmp4 - if(fieldDescriptor & 0x2000) + //Read tmp3 and tmp4 + if(fieldDescriptor & 0x2000) + { + if (i+3 >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp3 and tmp4"); + if (fieldDescriptor & 0x10000 && i+7 >= partsDataLen) + throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading high halves of tmp3 and tmp4"); + unsigned int tmp34; + tmp34 = (unsigned int)partsData[i + 0]; + tmp34 |= (unsigned int)partsData[i + 1] << 8; + if (fieldDescriptor & 0x10000) { - if (i+3 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading tmp3 and tmp4"); - if (fieldDescriptor & 0x10000 && i+7 >= partsDataLen) - throw ParseException(ParseException::Corrupt, "Ran past particle data buffer while loading high halves of tmp3 and tmp4"); - unsigned int tmp34; - tmp34 = (unsigned int)partsData[i + 0]; - tmp34 |= (unsigned int)partsData[i + 1] << 8; - if (fieldDescriptor & 0x10000) - { - tmp34 |= (unsigned int)partsData[i + 4] << 16; - tmp34 |= (unsigned int)partsData[i + 5] << 24; - } - particles[newIndex].tmp3 = int(tmp34); - tmp34 = (unsigned int)partsData[i + 2]; - tmp34 |= (unsigned int)partsData[i + 3] << 8; - if (fieldDescriptor & 0x10000) - { - tmp34 |= (unsigned int)partsData[i + 6] << 16; - tmp34 |= (unsigned int)partsData[i + 7] << 24; - } - particles[newIndex].tmp4 = int(tmp34); + tmp34 |= (unsigned int)partsData[i + 4] << 16; + tmp34 |= (unsigned int)partsData[i + 5] << 24; + } + particles[newIndex].tmp3 = int(tmp34); + tmp34 = (unsigned int)partsData[i + 2]; + tmp34 |= (unsigned int)partsData[i + 3] << 8; + if (fieldDescriptor & 0x10000) + { + tmp34 |= (unsigned int)partsData[i + 6] << 16; + tmp34 |= (unsigned int)partsData[i + 7] << 24; + } + particles[newIndex].tmp4 = int(tmp34); + i += 4; + if (fieldDescriptor & 0x10000) i += 4; - if (fieldDescriptor & 0x10000) - i += 4; - } + } - //Particle specific parsing: - switch(particles[newIndex].type) + //Particle specific parsing: + switch(particles[newIndex].type) + { + case PT_SOAP: + //Clear soap links, links will be added back in if soapLinkData is present + particles[newIndex].ctype &= ~6; + break; + case PT_BOMB: + if (particles[newIndex].tmp!=0 && savedVersion < 81) { - case PT_SOAP: - //Clear soap links, links will be added back in if soapLinkData is present - particles[newIndex].ctype &= ~6; - break; - case PT_BOMB: - if (particles[newIndex].tmp!=0 && savedVersion < 81) + particles[newIndex].type = PT_EMBR; + particles[newIndex].ctype = 0; + if (particles[newIndex].tmp==1) + particles[newIndex].tmp = 0; + } + break; + case PT_DUST: + if (particles[newIndex].life>0 && savedVersion < 81) + { + particles[newIndex].type = PT_EMBR; + particles[newIndex].ctype = (particles[newIndex].tmp2<<16) | (particles[newIndex].tmp<<8) | particles[newIndex].ctype; + particles[newIndex].tmp = 1; + } + break; + case PT_FIRW: + if (particles[newIndex].tmp>=2 && savedVersion < 81) + { + particles[newIndex].type = PT_EMBR; + particles[newIndex].ctype = Renderer::firwTableAt(particles[newIndex].tmp - 4).Pack(); + particles[newIndex].tmp = 1; + } + break; + case PT_PSTN: + if (savedVersion < 87 && particles[newIndex].ctype) + particles[newIndex].life = 1; + if (savedVersion < 91) + particles[newIndex].temp = 283.15f; + break; + case PT_FILT: + if (savedVersion < 89) + { + if (particles[newIndex].tmp<0 || particles[newIndex].tmp>3) + particles[newIndex].tmp = 6; + particles[newIndex].ctype = 0; + } + break; + case PT_QRTZ: + case PT_PQRT: + if (savedVersion < 89) + { + particles[newIndex].tmp2 = particles[newIndex].tmp; + particles[newIndex].tmp = particles[newIndex].ctype; + particles[newIndex].ctype = 0; + } + break; + case PT_PHOT: + if (savedVersion < 90) + { + particles[newIndex].flags |= FLAG_PHOTDECO; + } + break; + case PT_VINE: + if (savedVersion < 91) + { + particles[newIndex].tmp = 1; + } + break; + case PT_DLAY: + // correct DLAY temperature in older saves + // due to either the +.5f now done in DLAY (higher temps), or rounding errors in the old DLAY code (room temperature temps), + // the delay in all DLAY from older versions will always be one greater than it should + if (savedVersion < 91) + { + particles[newIndex].temp = particles[newIndex].temp - 1.0f; + } + break; + case PT_CRAY: + if (savedVersion < 91) + { + if (particles[newIndex].tmp2) { - particles[newIndex].type = PT_EMBR; - particles[newIndex].ctype = 0; - if (particles[newIndex].tmp==1) - particles[newIndex].tmp = 0; + particles[newIndex].ctype |= particles[newIndex].tmp2<<8; + particles[newIndex].tmp2 = 0; } - break; - case PT_DUST: - if (particles[newIndex].life>0 && savedVersion < 81) - { - particles[newIndex].type = PT_EMBR; - particles[newIndex].ctype = (particles[newIndex].tmp2<<16) | (particles[newIndex].tmp<<8) | particles[newIndex].ctype; - particles[newIndex].tmp = 1; - } - break; - case PT_FIRW: - if (particles[newIndex].tmp>=2 && savedVersion < 81) - { - particles[newIndex].type = PT_EMBR; - particles[newIndex].ctype = Renderer::firwTableAt(particles[newIndex].tmp - 4); - particles[newIndex].tmp = 1; - } - break; - case PT_PSTN: - if (savedVersion < 87 && particles[newIndex].ctype) - particles[newIndex].life = 1; - if (savedVersion < 91) - particles[newIndex].temp = 283.15f; - break; - case PT_FILT: - if (savedVersion < 89) - { - if (particles[newIndex].tmp<0 || particles[newIndex].tmp>3) - particles[newIndex].tmp = 6; - particles[newIndex].ctype = 0; - } - break; - case PT_QRTZ: - case PT_PQRT: - if (savedVersion < 89) - { - particles[newIndex].tmp2 = particles[newIndex].tmp; - particles[newIndex].tmp = particles[newIndex].ctype; - particles[newIndex].ctype = 0; - } - break; - case PT_PHOT: - if (savedVersion < 90) - { - particles[newIndex].flags |= FLAG_PHOTDECO; - } - break; - case PT_VINE: - if (savedVersion < 91) - { - particles[newIndex].tmp = 1; - } - break; - case PT_DLAY: - // correct DLAY temperature in older saves - // due to either the +.5f now done in DLAY (higher temps), or rounding errors in the old DLAY code (room temperature temps), - // the delay in all DLAY from older versions will always be one greater than it should - if (savedVersion < 91) - { - particles[newIndex].temp = particles[newIndex].temp - 1.0f; - } - break; - case PT_CRAY: - if (savedVersion < 91) - { - if (particles[newIndex].tmp2) - { - particles[newIndex].ctype |= particles[newIndex].tmp2<<8; - particles[newIndex].tmp2 = 0; - } - } - break; - case PT_CONV: - if (savedVersion < 91) - { - if (particles[newIndex].tmp) - { - particles[newIndex].ctype |= particles[newIndex].tmp<<8; - particles[newIndex].tmp = 0; - } - } - break; - case PT_PIPE: - case PT_PPIP: - if (savedVersion < 93 && !fakeNewerVersion) - { - if (particles[newIndex].ctype == 1) - particles[newIndex].tmp |= 0x00020000; //PFLAG_INITIALIZING - particles[newIndex].tmp |= (particles[newIndex].ctype-1)<<18; - particles[newIndex].ctype = particles[newIndex].tmp&0xFF; - } - break; - case PT_TSNS: - case PT_HSWC: - case PT_PSNS: - case PT_PUMP: - if (savedVersion < 93 && !fakeNewerVersion) + } + break; + case PT_CONV: + if (savedVersion < 91) + { + if (particles[newIndex].tmp) { + particles[newIndex].ctype |= particles[newIndex].tmp<<8; particles[newIndex].tmp = 0; } - break; - case PT_LIFE: - if (savedVersion < 96 && !fakeNewerVersion) - { - if (particles[newIndex].ctype >= 0 && particles[newIndex].ctype < NGOL) - { - particles[newIndex].tmp2 = particles[newIndex].tmp; - if (!particles[newIndex].dcolour) - particles[newIndex].dcolour = builtinGol[particles[newIndex].ctype].colour; - particles[newIndex].tmp = builtinGol[particles[newIndex].ctype].colour2; - } - } } - if (PressureInTmp3(particles[newIndex].type)) + break; + case PT_PIPE: + case PT_PPIP: + if (savedVersion < 93 && !fakeNewerVersion) { - // pavg[1] used to be saved as a u16, which PressureInTmp3 elements then treated as - // an i16. tmp3 is now saved as a u32, or as a u16 if it's small enough. PressureInTmp3 - // elements will never use the upper 16 bits, and should still treat the lower 16 bits - // as an i16, so they need sign extension. - auto tmp3 = (unsigned int)(particles[newIndex].tmp3); - if (tmp3 & 0x8000U) + if (particles[newIndex].ctype == 1) + particles[newIndex].tmp |= 0x00020000; //PFLAG_INITIALIZING + particles[newIndex].tmp |= (particles[newIndex].ctype-1)<<18; + particles[newIndex].ctype = particles[newIndex].tmp&0xFF; + } + break; + case PT_TSNS: + case PT_HSWC: + case PT_PSNS: + case PT_PUMP: + if (savedVersion < 93 && !fakeNewerVersion) + { + particles[newIndex].tmp = 0; + } + break; + case PT_LIFE: + if (savedVersion < 96 && !fakeNewerVersion) + { + if (particles[newIndex].ctype >= 0 && particles[newIndex].ctype < NGOL) { - tmp3 |= 0xFFFF0000U; - particles[newIndex].tmp3 = int(tmp3); + particles[newIndex].tmp2 = particles[newIndex].tmp; + if (!particles[newIndex].dcolour) + particles[newIndex].dcolour = builtinGol[particles[newIndex].ctype].colour.Pack(); + particles[newIndex].tmp = builtinGol[particles[newIndex].ctype].colour2.Pack(); } } - //note: PSv was used in version 77.0 and every version before, add something in PSv too if the element is that old - newIndex++; - partsCount++; } + if (PressureInTmp3(particles[newIndex].type)) + { + // pavg[1] used to be saved as a u16, which PressureInTmp3 elements then treated as + // an i16. tmp3 is now saved as a u32, or as a u16 if it's small enough. PressureInTmp3 + // elements will never use the upper 16 bits, and should still treat the lower 16 bits + // as an i16, so they need sign extension. + auto tmp3 = (unsigned int)(particles[newIndex].tmp3); + if (tmp3 & 0x8000U) + { + tmp3 |= 0xFFFF0000U; + particles[newIndex].tmp3 = int(tmp3); + } + } + //note: PSv was used in version 77.0 and every version before, add something in PSv too if the element is that old + newIndex++; + partsCount++; } } @@ -1241,21 +1154,23 @@ void GameSave::readOPS(const std::vector &data) } } +#define MTOS_EXPAND(str) #str +#define MTOS(str) MTOS_EXPAND(str) void GameSave::readPSv(const std::vector &dataVec) { Renderer::PopulateTables(); unsigned char * saveData = (unsigned char *)&dataVec[0]; auto dataLength = int(dataVec.size()); - int q,j,k,x,y,p=0, ver, pty, ty, legacy_beta=0; - int bx0=0, by0=0, bw, bh, w, h, y0 = 0, x0 = 0; + int q,p=0, ver, pty, ty, legacy_beta=0; + Vec2 blockP = { 0, 0 }; int new_format = 0, ttv = 0; std::vector tempSigns; char tempSignText[255]; sign tempSign("", 0, 0, sign::Left); - std::vector elements = GetElements(); + auto &elements = GetElements(); //New file header uses PSv, replacing fuC. This is to detect if the client uses a new save format for temperatures //This creates a problem for old clients, that display and "corrupt" error instead of a "newer version" error @@ -1298,18 +1213,10 @@ void GameSave::readPSv(const std::vector &dataVec) } } - bw = saveData[6]; - bh = saveData[7]; - if (bx0+bw > XRES/CELL) - bx0 = XRES/CELL - bw; - if (by0+bh > YRES/CELL) - by0 = YRES/CELL - bh; - if (bx0 < 0) - bx0 = 0; - if (by0 < 0) - by0 = 0; + auto blockS = Vec2{ int(saveData[6]), int(saveData[7]) }; + blockP = blockP.Clamp(blockS.OriginRect()); - if (saveData[5]!=CELL || bx0+bw>XRES/CELL || by0+bh>YRES/CELL) + if (saveData[5]!=CELL || blockP.X+blockS.X>XCELLS || blockP.Y+blockS.Y>YCELLS) throw ParseException(ParseException::InvalidDimensions, "Save too large"); int size = (unsigned)saveData[8]; size |= ((unsigned)saveData[9])<<8; @@ -1327,144 +1234,126 @@ void GameSave::readPSv(const std::vector &dataVec) default: throw ParseException(ParseException::Corrupt, String::Build("Cannot decompress: status ", int(status))); } - setSize(bw, bh); + setSize(blockS); const auto *data = reinterpret_cast(&bsonData[0]); dataLength = bsonData.size(); -#ifdef DEBUG - std::cout << "Parsing " << dataLength << " bytes of data, version " << ver << std::endl; -#endif + if constexpr (DEBUG) + { + std::cout << "Parsing " << dataLength << " bytes of data, version " << ver << std::endl; + } - if (dataLength < bw*bh) + if (dataLength < blockS.X*blockS.Y) throw ParseException(ParseException::Corrupt, "Save data corrupt (missing data)"); // normalize coordinates - x0 = bx0*CELL; - y0 = by0*CELL; - w = bw *CELL; - h = bh *CELL; + auto partS = blockS * CELL; + auto partP = blockP * CELL; if (ver<46) { gravityMode = 0; airMode = 0; } - std::vector particleIDMap(XRES * YRES, 0); + PlaneAdapter> particleIDMap(RES, 0); // load the required air state - for (y=by0; y()) + { + if (data[p]) { - if (data[p]) + //In old saves, ignore walls created by sign tool bug + //Not ignoring other invalid walls or invalid walls in new saves, so that any other bugs causing them are easier to notice, find and fix + if (ver>=44 && ver<71 && data[p]==O_WL_SIGN) { - //In old saves, ignore walls created by sign tool bug - //Not ignoring other invalid walls or invalid walls in new saves, so that any other bugs causing them are easier to notice, find and fix - if (ver>=44 && ver<71 && data[p]==O_WL_SIGN) - { - p++; - continue; - } - blockMap[y][x] = data[p]; - if (blockMap[y][x]==1) - blockMap[y][x]=WL_WALL; - else if (blockMap[y][x]==2) - blockMap[y][x]=WL_DESTROYALL; - else if (blockMap[y][x]==3) - blockMap[y][x]=WL_ALLOWLIQUID; - else if (blockMap[y][x]==4) - blockMap[y][x]=WL_FAN; - else if (blockMap[y][x]==5) - blockMap[y][x]=WL_STREAM; - else if (blockMap[y][x]==6) - blockMap[y][x]=WL_DETECT; - else if (blockMap[y][x]==7) - blockMap[y][x]=WL_EWALL; - else if (blockMap[y][x]==8) - blockMap[y][x]=WL_WALLELEC; - else if (blockMap[y][x]==9) - blockMap[y][x]=WL_ALLOWAIR; - else if (blockMap[y][x]==10) - blockMap[y][x]=WL_ALLOWPOWDER; - else if (blockMap[y][x]==11) - blockMap[y][x]=WL_ALLOWALLELEC; - else if (blockMap[y][x]==12) - blockMap[y][x]=WL_EHOLE; - else if (blockMap[y][x]==13) - blockMap[y][x]=WL_ALLOWGAS; - - if (ver>=44) - { - /* The numbers used to save walls were changed, starting in v44. - * The new numbers are ignored for older versions due to some corruption of bmap in saves from older versions. - */ - if (blockMap[y][x]==O_WL_WALLELEC) - blockMap[y][x]=WL_WALLELEC; - else if (blockMap[y][x]==O_WL_EWALL) - blockMap[y][x]=WL_EWALL; - else if (blockMap[y][x]==O_WL_DETECT) - blockMap[y][x]=WL_DETECT; - else if (blockMap[y][x]==O_WL_STREAM) - blockMap[y][x]=WL_STREAM; - else if (blockMap[y][x]==O_WL_FAN||blockMap[y][x]==O_WL_FANHELPER) - blockMap[y][x]=WL_FAN; - else if (blockMap[y][x]==O_WL_ALLOWLIQUID) - blockMap[y][x]=WL_ALLOWLIQUID; - else if (blockMap[y][x]==O_WL_DESTROYALL) - blockMap[y][x]=WL_DESTROYALL; - else if (blockMap[y][x]==O_WL_ERASE) - blockMap[y][x]=WL_ERASE; - else if (blockMap[y][x]==O_WL_WALL) - blockMap[y][x]=WL_WALL; - else if (blockMap[y][x]==O_WL_ALLOWAIR) - blockMap[y][x]=WL_ALLOWAIR; - else if (blockMap[y][x]==O_WL_ALLOWSOLID) - blockMap[y][x]=WL_ALLOWPOWDER; - else if (blockMap[y][x]==O_WL_ALLOWALLELEC) - blockMap[y][x]=WL_ALLOWALLELEC; - else if (blockMap[y][x]==O_WL_EHOLE) - blockMap[y][x]=WL_EHOLE; - else if (blockMap[y][x]==O_WL_ALLOWGAS) - blockMap[y][x]=WL_ALLOWGAS; - else if (blockMap[y][x]==O_WL_GRAV) - blockMap[y][x]=WL_GRAV; - else if (blockMap[y][x]==O_WL_ALLOWENERGY) - blockMap[y][x]=WL_ALLOWENERGY; - } - - if (blockMap[y][x] >= UI_WALLCOUNT) - blockMap[y][x] = 0; + p++; + continue; + } + auto bm = data[p]; + switch (bm) + { + case 1: bm = WL_WALL ; break; + case 2: bm = WL_DESTROYALL ; break; + case 3: bm = WL_ALLOWLIQUID ; break; + case 4: bm = WL_FAN ; break; + case 5: bm = WL_STREAM ; break; + case 6: bm = WL_DETECT ; break; + case 7: bm = WL_EWALL ; break; + case 8: bm = WL_WALLELEC ; break; + case 9: bm = WL_ALLOWAIR ; break; + case 10: bm = WL_ALLOWPOWDER ; break; + case 11: bm = WL_ALLOWALLELEC; break; + case 12: bm = WL_EHOLE ; break; + case 13: bm = WL_ALLOWGAS ; break; } - p++; + if (ver>=44) + { + /* The numbers used to save walls were changed, starting in v44. + * The new numbers are ignored for older versions due to some corruption of bmap in saves from older versions. + */ + switch (bm) + { + case O_WL_WALLELEC: bm = WL_WALLELEC ; break; + case O_WL_EWALL: bm = WL_EWALL ; break; + case O_WL_DETECT: bm = WL_DETECT ; break; + case O_WL_STREAM: bm = WL_STREAM ; break; + case O_WL_FAN: + case O_WL_FANHELPER: bm = WL_FAN ; break; + case O_WL_ALLOWLIQUID: bm = WL_ALLOWLIQUID ; break; + case O_WL_DESTROYALL: bm = WL_DESTROYALL ; break; + case O_WL_ERASE: bm = WL_ERASE ; break; + case O_WL_WALL: bm = WL_WALL ; break; + case O_WL_ALLOWAIR: bm = WL_ALLOWAIR ; break; + case O_WL_ALLOWSOLID: bm = WL_ALLOWPOWDER ; break; + case O_WL_ALLOWALLELEC: bm = WL_ALLOWALLELEC; break; + case O_WL_EHOLE: bm = WL_EHOLE ; break; + case O_WL_ALLOWGAS: bm = WL_ALLOWGAS ; break; + case O_WL_GRAV: bm = WL_GRAV ; break; + case O_WL_ALLOWENERGY: bm = WL_ALLOWENERGY ; break; + } + } + + if (bm >= UI_WALLCOUNT) + bm = 0; + blockMap[bpos] = bm; } - for (y=by0; y=44 && data[(y-by0)*bw+(x-bx0)]==O_WL_FAN)) - { - if (p >= dataLength) - throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - fanVelX[y][x] = (data[p++]-127.0f)/64.0f; - } - for (y=by0; y=44 && data[(y-by0)*bw+(x-bx0)]==O_WL_FAN)) - { - if (p >= dataLength) - throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - fanVelY[y][x] = (data[p++]-127.0f)/64.0f; - } - // load the particle map - int i = 0; - k = 0; - pty = p; - for (y=y0; y()) + { + // TODO: use PlaneAdapter> once we're C++20 + auto dataPlane = PlaneAdapter>(blockS, std::in_place, data, blockS.X * blockS.Y); + if (dataPlane[bpos - blockP]==4||(ver>=44 && dataPlane[bpos - blockP]==O_WL_FAN)) { if (p >= dataLength) throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - j=data[p++]; - if (j >= PT_NUM) { + fanVelX[bpos] = (data[p++]-127.0f)/64.0f; + } + } + for (auto bpos : RectSized(blockP, blockS).Range()) + { + // TODO: use PlaneAdapter> once we're C++20 + auto dataPlane = PlaneAdapter>(blockS, std::in_place, data, blockS.X * blockS.Y); + if (dataPlane[bpos - blockP]==4||(ver>=44 && dataPlane[bpos - blockP]==O_WL_FAN)) + { + if (p >= dataLength) + throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); + fanVelY[bpos] = (data[p++]-127.0f)/64.0f; + } + } + + // load the particle map + { + auto k = 0; + pty = p; + for (auto pos : RectSized(partP, partS).Range()) + { + if (p >= dataLength) + throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); + auto j=data[p++]; + if (int(j) >= PT_NUM) { // not possible these days since PMAPBITS >= 8 j = PT_DUST;//throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); } if (j) @@ -1481,17 +1370,18 @@ void GameSave::readPSv(const std::vector &dataVec) particles[k].ctype = 0; if (j==PT_BIZR || j==PT_BIZRG || j==PT_BIZRS) particles[k].ctype = 0x47FFFF; - particles[k].x = (float)x; - particles[k].y = (float)y; - particleIDMap[(x-x0)+(y-y0)*w] = k+1; + particles[k].x = (float)pos.X; + particles[k].y = (float)pos.Y; + particleIDMap[pos - partP] = k+1; particlesCount = ++k; } } + } // load particle properties - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { i--; @@ -1506,9 +1396,9 @@ void GameSave::readPSv(const std::vector &dataVec) p += 2; } } - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=44) { @@ -1533,9 +1423,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } if (ver>=44) { - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (p >= dataLength) { @@ -1561,11 +1451,13 @@ void GameSave::readPSv(const std::vector &dataVec) } } } + // TODO: use PlaneAdapter> once we're C++20 + auto dataPlanePty = PlaneAdapter>(partS, std::in_place, data + pty, partS.X * partS.Y); if (ver>=53) { - for (j=0; j()) { - i = particleIDMap[j]; - ty = data[pty+j]; + auto i = particleIDMap[pos]; + ty = dataPlanePty[pos]; if (i && (ty==PT_PBCN || (ty==PT_TRON && ver>=77))) { if (p >= dataLength) @@ -1578,9 +1470,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } //Read ALPHA component - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=49) { @@ -1596,9 +1488,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } //Read RED component - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=49) { @@ -1614,9 +1506,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } //Read GREEN component - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=49) { @@ -1632,9 +1524,9 @@ void GameSave::readPSv(const std::vector &dataVec) } } //Read BLUE component - for (j=0; j()) { - i = particleIDMap[j]; + auto i = particleIDMap[pos]; if (i) { if (ver>=49) { @@ -1649,10 +1541,10 @@ void GameSave::readPSv(const std::vector &dataVec) } } } - for (j=0; j()) { - i = particleIDMap[j]; - ty = data[pty+j]; + auto i = particleIDMap[pos]; + ty = dataPlanePty[pos]; if (i) { if (ver>=34&&legacy_beta==0) @@ -1693,11 +1585,11 @@ void GameSave::readPSv(const std::vector &dataVec) } } } - for (j=0; j()) { + auto i = particleIDMap[pos]; int gnum = 0; - i = particleIDMap[j]; - ty = data[pty+j]; + ty = dataPlanePty[pos]; if (i && (ty==PT_CLNE || (ty==PT_PCLN && ver>=43) || (ty==PT_BCLN && ver>=44) || (ty==PT_SPRK && ver>=21) || (ty==PT_LAVA && ver>=34) || (ty==PT_PIPE && ver>=43) || (ty==PT_LIFE && ver>=51) || (ty==PT_PBCN && ver>=52) || (ty==PT_WIRE && ver>=55) || (ty==PT_STOR && ver>=59) || (ty==PT_CONV && ver>=60))) { if (p >= dataLength) @@ -1723,8 +1615,6 @@ void GameSave::readPSv(const std::vector &dataVec) if (ver<48 && (ty==OLD_PT_WIND || (ty==PT_BRAY&&particles[i-1].life==0))) { // Replace invisible particles with something sensible and add decoration to hide it - x = (int)(particles[i-1].x+0.5f); - y = (int)(particles[i-1].y+0.5f); particles[i-1].dcolour = 0xFF000000; particles[i-1].type = PT_DMND; } @@ -1754,8 +1644,8 @@ void GameSave::readPSv(const std::vector &dataVec) if (particles[i-1].ctype >= 0 && particles[i-1].ctype < NGOL) { if (!particles[i-1].dcolour) - particles[i-1].dcolour = builtinGol[particles[i-1].ctype].colour; - particles[i-1].tmp = builtinGol[particles[i-1].ctype].colour2; + particles[i-1].dcolour = builtinGol[particles[i-1].ctype].colour.Pack(); + particles[i-1].tmp = builtinGol[particles[i-1].ctype].colour2.Pack(); } } if(ty==PT_LCRY){ @@ -1803,7 +1693,7 @@ void GameSave::readPSv(const std::vector &dataVec) if (particles[i-1].type==PT_FIRW && particles[i-1].tmp>=2) { particles[i-1].type = PT_EMBR; - particles[i-1].ctype = Renderer::firwTableAt(particles[i-1].tmp-4); + particles[i-1].ctype = Renderer::firwTableAt(particles[i-1].tmp-4).Pack(); particles[i-1].tmp = 1; } } @@ -1855,26 +1745,35 @@ void GameSave::readPSv(const std::vector &dataVec) if (p == dataLength) // no sign data, "version 1" PSv return; - j = data[p++]; - for (i=0; i dataLength) throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - x = data[p++]; - x |= ((unsigned)data[p++])<<8; - tempSign.x = x+x0; - x = data[p++]; - x |= ((unsigned)data[p++])<<8; - tempSign.y = x+y0; - x = data[p++]; - tempSign.ju = (sign::Justification)x; - x = data[p++]; - if (p+x > dataLength) - throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); - if(x>254) - x = 254; - memcpy(tempSignText, &data[0]+p, x); - tempSignText[x] = 0; + { + auto x = data[p++]; + x |= ((unsigned)data[p++])<<8; + tempSign.x = x+partP.X; + } + { + auto y = data[p++]; + y |= ((unsigned)data[p++])<<8; + tempSign.y = y+partP.Y; + } + { + auto ju = data[p++]; + tempSign.ju = (sign::Justification)ju; + } + { + auto l = data[p++]; + if (p+l > dataLength) + throw ParseException(ParseException::Corrupt, "Not enough data at line " MTOS(__LINE__) " in " MTOS(__FILE__)); + if(l>254) + l = 254; + memcpy(tempSignText, &data[0]+p, l); + tempSignText[l] = 0; + p += l; + } tempSign.text = format::CleanString(ByteString(tempSignText).FromUtf8(), true, true, true).Substr(0, 45); if (tempSign.text == "{t}") { @@ -1885,7 +1784,6 @@ void GameSave::readPSv(const std::vector &dataVec) tempSign.text = "Pressure: {p}"; } tempSigns.push_back(tempSign); - p += x; } for (size_t i = 0; i < tempSigns.size(); i++) @@ -1895,93 +1793,97 @@ void GameSave::readPSv(const std::vector &dataVec) signs.push_back(tempSigns[i]); } } - -// restrict the minimum version this save can be opened with -#define RESTRICTVERSION(major, minor) if ((major) > minimumMajorVersion || (((major) == minimumMajorVersion && (minor) > minimumMinorVersion))) {\ - minimumMajorVersion = major;\ - minimumMinorVersion = minor;\ -} +#undef MTOS +#undef MTOS_EXPAND std::pair> GameSave::serialiseOPS() const { - int blockX, blockY, blockW, blockH, fullX, fullY, fullW, fullH; - int x, y, i; // minimum version this save is compatible with // when building, this number may be increased depending on what elements are used // or what properties are detected int minimumMajorVersion = 90, minimumMinorVersion = 2; + auto RESTRICTVERSION = [&minimumMajorVersion, &minimumMinorVersion](int major, int minor) { + // restrict the minimum version this save can be opened with + if (major > minimumMajorVersion || ((major == minimumMajorVersion && minor > minimumMinorVersion))) + { + minimumMajorVersion = major; + minimumMinorVersion = minor; + } + }; //Get coords in blocks - blockX = 0;//orig_x0/CELL; - blockY = 0;//orig_y0/CELL; + auto blockP = Vec2{ 0, 0 }; //Snap full coords to block size - fullX = blockX*CELL; - fullY = blockY*CELL; + auto partP = blockP * CELL; //Original size + offset of original corner from snapped corner, rounded up by adding CELL-1 - blockW = blockWidth;//(blockWidth-fullX+CELL-1)/CELL; - blockH = blockHeight;//(blockHeight-fullY+CELL-1)/CELL; - fullW = blockW*CELL; - fullH = blockH*CELL; + auto blockS = blockSize; + auto partS = blockS * CELL; // Copy fan and wall data - std::vector wallData(blockWidth*blockHeight); + PlaneAdapter> wallData(blockSize); bool hasWallData = false; - std::vector fanData(blockWidth*blockHeight*2); - std::vector pressData(blockWidth*blockHeight*2); - std::vector vxData(blockWidth*blockHeight*2); - std::vector vyData(blockWidth*blockHeight*2); - std::vector ambientData(blockWidth*blockHeight*2, 0); - unsigned int wallDataLen = blockWidth*blockHeight, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0; + std::vector fanData(blockSize.X*blockSize.Y*2); + std::vector pressData(blockSize.X*blockSize.Y*2); + std::vector vxData(blockSize.X*blockSize.Y*2); + std::vector vyData(blockSize.X*blockSize.Y*2); + std::vector ambientData(blockSize.X*blockSize.Y*2, 0); + // TODO: have a separate vector with two PlaneAdapter>s over it once we're C++20 + PlaneAdapter> blockAirData({ blockSize.X, blockSize.Y * 2 }); + unsigned int wallDataLen = blockSize.X*blockSize.Y, fanDataLen = 0, pressDataLen = 0, vxDataLen = 0, vyDataLen = 0, ambientDataLen = 0; - for (x = blockX; x < blockX+blockW; x++) + for (auto bpos : RectSized(blockP, blockS).Range()) { - for (y = blockY; y < blockY+blockH; y++) + wallData[bpos - blockP] = blockMap[bpos]; + if (blockMap[bpos]) + hasWallData = true; + + if (hasPressure) { - wallData[(y-blockY)*blockW+(x-blockX)] = blockMap[y][x]; - if (blockMap[y][x]) - hasWallData = true; + //save pressure and x/y velocity grids + float pres = std::max(-255.0f,std::min(255.0f,pressure[bpos]))+256.0f; + float velX = std::max(-255.0f,std::min(255.0f,velocityX[bpos]))+256.0f; + float velY = std::max(-255.0f,std::min(255.0f,velocityY[bpos]))+256.0f; + pressData[pressDataLen++] = (unsigned char)((int)(pres*128)&0xFF); + pressData[pressDataLen++] = (unsigned char)((int)(pres*128)>>8); - if (hasPressure) + vxData[vxDataLen++] = (unsigned char)((int)(velX*128)&0xFF); + vxData[vxDataLen++] = (unsigned char)((int)(velX*128)>>8); + + vyData[vyDataLen++] = (unsigned char)((int)(velY*128)&0xFF); + vyData[vyDataLen++] = (unsigned char)((int)(velY*128)>>8); + + blockAirData[bpos - blockP] = blockAir[bpos]; + blockAirData[(bpos - blockP) + Vec2{ 0, blockS.Y }] = blockAirh[bpos]; + } + + if (hasAmbientHeat) + { + int tempTemp = (int)(ambientHeat[bpos]+0.5f); + ambientData[ambientDataLen++] = tempTemp; + ambientData[ambientDataLen++] = tempTemp >> 8; + } + + if (blockMap[bpos] == WL_FAN) + { { - //save pressure and x/y velocity grids - float pres = std::max(-255.0f,std::min(255.0f,pressure[y][x]))+256.0f; - float velX = std::max(-255.0f,std::min(255.0f,velocityX[y][x]))+256.0f; - float velY = std::max(-255.0f,std::min(255.0f,velocityY[y][x]))+256.0f; - pressData[pressDataLen++] = (unsigned char)((int)(pres*128)&0xFF); - pressData[pressDataLen++] = (unsigned char)((int)(pres*128)>>8); - - vxData[vxDataLen++] = (unsigned char)((int)(velX*128)&0xFF); - vxData[vxDataLen++] = (unsigned char)((int)(velX*128)>>8); - - vyData[vyDataLen++] = (unsigned char)((int)(velY*128)&0xFF); - vyData[vyDataLen++] = (unsigned char)((int)(velY*128)>>8); - } - - if (hasAmbientHeat) - { - int tempTemp = (int)(ambientHeat[y][x]+0.5f); - ambientData[ambientDataLen++] = tempTemp; - ambientData[ambientDataLen++] = tempTemp >> 8; - } - - if (blockMap[y][x] == WL_FAN) - { - i = (int)(fanVelX[y][x]*64.0f+127.5f); - if (i<0) i=0; - if (i>255) i=255; - fanData[fanDataLen++] = i; - i = (int)(fanVelY[y][x]*64.0f+127.5f); + auto i = (int)(fanVelX[bpos]*64.0f+127.5f); if (i<0) i=0; if (i>255) i=255; fanData[fanDataLen++] = i; } - else if (blockMap[y][x] == WL_STASIS) { - RESTRICTVERSION(94, 0); + auto i = (int)(fanVelY[bpos]*64.0f+127.5f); + if (i<0) i=0; + if (i>255) i=255; + fanData[fanDataLen++] = i; } } + else if (blockMap[bpos] == WL_STASIS) + { + RESTRICTVERSION(94, 0); + } } //Index positions of all particles, using linked lists @@ -1989,48 +1891,42 @@ std::pair> GameSave::serialiseOPS() const //partsPosLastMap is pmap for the last particle in each position //partsPosCount is the number of particles in each position //partsPosLink contains, for each particle, (i<<8)|1 of the next particle in the same position - std::vector partsPosFirstMap(fullW*fullH, 0); - std::vector partsPosLastMap(fullW*fullH, 0); - std::vector partsPosCount(fullW*fullH, 0); + PlaneAdapter> partsPosFirstMap(partS, 0); + PlaneAdapter> partsPosLastMap(partS, 0); + PlaneAdapter> partsPosCount(partS, 0); std::vector partsPosLink(NPART, 0); unsigned int soapCount = 0; - for(i = 0; i < particlesCount; i++) + for(auto i = 0; i < particlesCount; i++) { if(particles[i].type) { - x = (int)(particles[i].x+0.5f); - y = (int)(particles[i].y+0.5f); + auto pos = Vec2{ (int)(particles[i].x+0.5f), (int)(particles[i].y+0.5f) }; //Coordinates relative to top left corner of saved area - x -= fullX; - y -= fullY; - if (!partsPosFirstMap[y*fullW + x]) + if (!partsPosFirstMap[pos - partP]) { //First entry in list - partsPosFirstMap[y*fullW + x] = (i<<8)|1; - partsPosLastMap[y*fullW + x] = (i<<8)|1; + partsPosFirstMap[pos - partP] = (i<<8)|1; + partsPosLastMap[pos - partP] = (i<<8)|1; } else { //Add to end of list - partsPosLink[partsPosLastMap[y*fullW + x]>>8] = (i<<8)|1;//link to current end of list - partsPosLastMap[y*fullW + x] = (i<<8)|1;//set as new end of list + partsPosLink[partsPosLastMap[pos - partP]>>8] = (i<<8)|1;//link to current end of list + partsPosLastMap[pos - partP] = (i<<8)|1;//set as new end of list } - partsPosCount[y*fullW + x]++; + partsPosCount[pos - partP]++; } } //Store number of particles in each position - std::vector partsPosData(fullW*fullH*3); + std::vector partsPosData(partS.X * partS.Y * 3); unsigned int partsPosDataLen = 0; - for (y=0;y()) { - for (x=0;x>16; - partsPosData[partsPosDataLen++] = (posCount&0x0000FF00)>>8; - partsPosData[partsPosDataLen++] = (posCount&0x000000FF); - } + unsigned int posCount = partsPosCount[pos]; + partsPosData[partsPosDataLen++] = (posCount&0x00FF0000)>>16; + partsPosData[partsPosDataLen++] = (posCount&0x0000FF00)>>8; + partsPosData[partsPosDataLen++] = (posCount&0x000000FF); } //Copy parts data @@ -2046,284 +1942,283 @@ std::pair> GameSave::serialiseOPS() const That way, if we ever need a 25th bit, we won't have to change the save format */ + auto &elements = GetElements(); + auto &possiblyCarriesType = Particle::PossiblyCarriesType(); + auto &properties = Particle::GetProperties(); // Allocate enough space to store all Particles and 3 bytes on top of that per Particle, for the field descriptors. // In practice, a Particle will never need as much space in the save as in memory; this is just an upper bound to simplify allocation. std::vector partsData(NPART * (sizeof(Particle)+3)); unsigned int partsDataLen = 0; std::vector partsSaveIndex(NPART); unsigned int partsCount = 0; - std::fill(&partsSaveIndex[0], &partsSaveIndex[NPART], 0); - for (y=0;y()) { - for (x=0;x>8; + + //Store saved particle index+1 for this partsptr index (0 means not saved) + partsSaveIndex[i] = (partsCount++) + 1; + + //Type (required) + partsData[partsDataLen++] = particles[i].type; + + //Location of the field descriptor + int fieldDesc3Loc = 0; + int fieldDescLoc = partsDataLen++; + partsDataLen++; + + auto tmp3 = (unsigned int)(particles[i].tmp3); + auto tmp4 = (unsigned int)(particles[i].tmp4); + if ((tmp3 || tmp4) && (!PressureInTmp3(particles[i].type) || hasPressure)) { - unsigned int fieldDesc = 0; - int tempTemp, vTemp; - - //Turn pmap entry into a particles index - i = i>>8; - - //Store saved particle index+1 for this partsptr index (0 means not saved) - partsSaveIndex[i] = (partsCount++) + 1; - - //Type (required) - partsData[partsDataLen++] = particles[i].type; - - //Location of the field descriptor - int fieldDesc3Loc = 0; - int fieldDescLoc = partsDataLen++; - partsDataLen++; - - auto tmp3 = (unsigned int)(particles[i].tmp3); - auto tmp4 = (unsigned int)(particles[i].tmp4); - if ((tmp3 || tmp4) && (!PressureInTmp3(particles[i].type) || hasPressure)) + fieldDesc |= 1 << 13; + // The tmp3 of PressureInTmp3 elements is okay to truncate because the loading code + // sign extends it anyway, expecting the value to not be higher in magnitude than + // 256 (max pressure value) * 64 (tmp3 multiplicative bias). + if (((tmp3 >> 16) || (tmp4 >> 16)) && !PressureInTmp3(particles[i].type)) { - fieldDesc |= 1 << 13; - // The tmp3 of PressureInTmp3 elements is okay to truncate because the loading code - // sign extends it anyway, expecting the value to not be higher in magnitude than - // 256 (max pressure value) * 64 (tmp3 multiplicative bias). - if (((tmp3 >> 16) || (tmp4 >> 16)) && !PressureInTmp3(particles[i].type)) + fieldDesc |= 1 << 15; + fieldDesc |= 1 << 16; + RESTRICTVERSION(97, 0); + } + } + + // Extra type byte if necessary + if (particles[i].type & 0xFF00) + { + partsData[partsDataLen++] = particles[i].type >> 8; + fieldDesc |= 1 << 14; + RESTRICTVERSION(93, 0); + } + + //Extra Temperature (2nd byte optional, 1st required), 1 to 2 bytes + //Store temperature as an offset of 21C(294.15K) or go into a 16byte int and store the whole thing + if(fabs(particles[i].temp-294.15f)<127) + { + tempTemp = int(floor(particles[i].temp-294.15f+0.5f)); + partsData[partsDataLen++] = tempTemp; + } + else + { + fieldDesc |= 1; + tempTemp = (int)(particles[i].temp+0.5f); + partsData[partsDataLen++] = tempTemp; + partsData[partsDataLen++] = tempTemp >> 8; + } + + if (fieldDesc & (1 << 15)) + { + fieldDesc3Loc = partsDataLen++; + } + + //Life (optional), 1 to 2 bytes + if(particles[i].life) + { + int life = particles[i].life; + if (life > 0xFFFF) + life = 0xFFFF; + else if (life < 0) + life = 0; + fieldDesc |= 1 << 1; + partsData[partsDataLen++] = life; + if (life & 0xFF00) + { + fieldDesc |= 1 << 2; + partsData[partsDataLen++] = life >> 8; + } + } + + //Tmp (optional), 1, 2, or 4 bytes + if(particles[i].tmp) + { + fieldDesc |= 1 << 3; + partsData[partsDataLen++] = particles[i].tmp; + if(particles[i].tmp & 0xFFFFFF00) + { + fieldDesc |= 1 << 4; + partsData[partsDataLen++] = particles[i].tmp >> 8; + if(particles[i].tmp & 0xFFFF0000) { - fieldDesc |= 1 << 15; - fieldDesc |= 1 << 16; - RESTRICTVERSION(97, 0); + fieldDesc |= 1 << 12; + partsData[partsDataLen++] = (particles[i].tmp&0xFF000000)>>24; + partsData[partsDataLen++] = (particles[i].tmp&0x00FF0000)>>16; } } + } - // Extra type byte if necessary - if (particles[i].type & 0xFF00) + //Ctype (optional), 1 or 4 bytes + if(particles[i].ctype) + { + fieldDesc |= 1 << 5; + partsData[partsDataLen++] = particles[i].ctype; + if(particles[i].ctype & 0xFFFFFF00) + { + fieldDesc |= 1 << 9; + partsData[partsDataLen++] = (particles[i].ctype&0xFF000000)>>24; + partsData[partsDataLen++] = (particles[i].ctype&0x00FF0000)>>16; + partsData[partsDataLen++] = (particles[i].ctype&0x0000FF00)>>8; + } + } + + //Dcolour (optional), 4 bytes + if(particles[i].dcolour && (particles[i].dcolour & 0xFF000000 || particles[i].type == PT_LIFE)) + { + fieldDesc |= 1 << 6; + partsData[partsDataLen++] = (particles[i].dcolour&0xFF000000)>>24; + partsData[partsDataLen++] = (particles[i].dcolour&0x00FF0000)>>16; + partsData[partsDataLen++] = (particles[i].dcolour&0x0000FF00)>>8; + partsData[partsDataLen++] = (particles[i].dcolour&0x000000FF); + } + + //VX (optional), 1 byte + if(fabs(particles[i].vx) > 0.001f) + { + fieldDesc |= 1 << 7; + vTemp = (int)(particles[i].vx*16.0f+127.5f); + if (vTemp<0) vTemp=0; + if (vTemp>255) vTemp=255; + partsData[partsDataLen++] = vTemp; + } + + //VY (optional), 1 byte + if(fabs(particles[i].vy) > 0.001f) + { + fieldDesc |= 1 << 8; + vTemp = (int)(particles[i].vy*16.0f+127.5f); + if (vTemp<0) vTemp=0; + if (vTemp>255) vTemp=255; + partsData[partsDataLen++] = vTemp; + } + + //Tmp2 (optional), 1 or 2 bytes + if(particles[i].tmp2) + { + fieldDesc |= 1 << 10; + partsData[partsDataLen++] = particles[i].tmp2; + if(particles[i].tmp2 & 0xFF00) + { + fieldDesc |= 1 << 11; + partsData[partsDataLen++] = particles[i].tmp2 >> 8; + } + } + + //tmp3 and tmp4, 4 bytes + if (fieldDesc & (1 << 13)) + { + partsData[partsDataLen++] = tmp3 ; + partsData[partsDataLen++] = tmp3 >> 8; + partsData[partsDataLen++] = tmp4 ; + partsData[partsDataLen++] = tmp4 >> 8; + if (fieldDesc & (1 << 16)) + { + partsData[partsDataLen++] = tmp3 >> 16; + partsData[partsDataLen++] = tmp3 >> 24; + partsData[partsDataLen++] = tmp4 >> 16; + partsData[partsDataLen++] = tmp4 >> 24; + } + } + + //Write the field descriptor + partsData[fieldDescLoc] = fieldDesc; + partsData[fieldDescLoc+1] = fieldDesc>>8; + if (fieldDesc & (1 << 15)) + { + partsData[fieldDesc3Loc] = fieldDesc>>16; + } + + if (particles[i].type == PT_SOAP) + soapCount++; + + if (particles[i].type == PT_RPEL && particles[i].ctype) + { + RESTRICTVERSION(91, 4); + } + else if (particles[i].type == PT_NWHL && particles[i].tmp) + { + RESTRICTVERSION(91, 5); + } + if (particles[i].type == PT_HEAC || particles[i].type == PT_SAWD || particles[i].type == PT_POLO + || particles[i].type == PT_RFRG || particles[i].type == PT_RFGL || particles[i].type == PT_LSNS) + { + RESTRICTVERSION(92, 0); + } + else if ((particles[i].type == PT_FRAY || particles[i].type == PT_INVIS) && particles[i].tmp) + { + RESTRICTVERSION(92, 0); + } + else if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP) + { + RESTRICTVERSION(93, 0); + } + if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS + || particles[i].type == PT_HSWC || particles[i].type == PT_PUMP) + { + if (particles[i].tmp == 1) { - partsData[partsDataLen++] = particles[i].type >> 8; - fieldDesc |= 1 << 14; RESTRICTVERSION(93, 0); } - - //Extra Temperature (2nd byte optional, 1st required), 1 to 2 bytes - //Store temperature as an offset of 21C(294.15K) or go into a 16byte int and store the whole thing - if(fabs(particles[i].temp-294.15f)<127) + } + if (PMAPBITS > 8) + { + for (auto index : possiblyCarriesType) { - tempTemp = int(floor(particles[i].temp-294.15f+0.5f)); - partsData[partsDataLen++] = tempTemp; - } - else - { - fieldDesc |= 1; - tempTemp = (int)(particles[i].temp+0.5f); - partsData[partsDataLen++] = tempTemp; - partsData[partsDataLen++] = tempTemp >> 8; - } - - if (fieldDesc & (1 << 15)) - { - fieldDesc3Loc = partsDataLen++; - } - - //Life (optional), 1 to 2 bytes - if(particles[i].life) - { - int life = particles[i].life; - if (life > 0xFFFF) - life = 0xFFFF; - else if (life < 0) - life = 0; - fieldDesc |= 1 << 1; - partsData[partsDataLen++] = life; - if (life & 0xFF00) + if (elements[particles[i].type].CarriesTypeIn & (1U << index)) { - fieldDesc |= 1 << 2; - partsData[partsDataLen++] = life >> 8; - } - } - - //Tmp (optional), 1, 2, or 4 bytes - if(particles[i].tmp) - { - fieldDesc |= 1 << 3; - partsData[partsDataLen++] = particles[i].tmp; - if(particles[i].tmp & 0xFFFFFF00) - { - fieldDesc |= 1 << 4; - partsData[partsDataLen++] = particles[i].tmp >> 8; - if(particles[i].tmp & 0xFFFF0000) + auto *prop = reinterpret_cast(reinterpret_cast(&particles[i]) + properties[index].Offset); + if (TYP(*prop) > 0xFF) { - fieldDesc |= 1 << 12; - partsData[partsDataLen++] = (particles[i].tmp&0xFF000000)>>24; - partsData[partsDataLen++] = (particles[i].tmp&0x00FF0000)>>16; + RESTRICTVERSION(93, 0); } } } - - //Ctype (optional), 1 or 4 bytes - if(particles[i].ctype) - { - fieldDesc |= 1 << 5; - partsData[partsDataLen++] = particles[i].ctype; - if(particles[i].ctype & 0xFFFFFF00) - { - fieldDesc |= 1 << 9; - partsData[partsDataLen++] = (particles[i].ctype&0xFF000000)>>24; - partsData[partsDataLen++] = (particles[i].ctype&0x00FF0000)>>16; - partsData[partsDataLen++] = (particles[i].ctype&0x0000FF00)>>8; - } - } - - //Dcolour (optional), 4 bytes - if(particles[i].dcolour && (particles[i].dcolour & 0xFF000000 || particles[i].type == PT_LIFE)) - { - fieldDesc |= 1 << 6; - partsData[partsDataLen++] = (particles[i].dcolour&0xFF000000)>>24; - partsData[partsDataLen++] = (particles[i].dcolour&0x00FF0000)>>16; - partsData[partsDataLen++] = (particles[i].dcolour&0x0000FF00)>>8; - partsData[partsDataLen++] = (particles[i].dcolour&0x000000FF); - } - - //VX (optional), 1 byte - if(fabs(particles[i].vx) > 0.001f) - { - fieldDesc |= 1 << 7; - vTemp = (int)(particles[i].vx*16.0f+127.5f); - if (vTemp<0) vTemp=0; - if (vTemp>255) vTemp=255; - partsData[partsDataLen++] = vTemp; - } - - //VY (optional), 1 byte - if(fabs(particles[i].vy) > 0.001f) - { - fieldDesc |= 1 << 8; - vTemp = (int)(particles[i].vy*16.0f+127.5f); - if (vTemp<0) vTemp=0; - if (vTemp>255) vTemp=255; - partsData[partsDataLen++] = vTemp; - } - - //Tmp2 (optional), 1 or 2 bytes - if(particles[i].tmp2) - { - fieldDesc |= 1 << 10; - partsData[partsDataLen++] = particles[i].tmp2; - if(particles[i].tmp2 & 0xFF00) - { - fieldDesc |= 1 << 11; - partsData[partsDataLen++] = particles[i].tmp2 >> 8; - } - } - - //tmp3 and tmp4, 4 bytes - if (fieldDesc & (1 << 13)) - { - partsData[partsDataLen++] = tmp3 ; - partsData[partsDataLen++] = tmp3 >> 8; - partsData[partsDataLen++] = tmp4 ; - partsData[partsDataLen++] = tmp4 >> 8; - if (fieldDesc & (1 << 16)) - { - partsData[partsDataLen++] = tmp3 >> 16; - partsData[partsDataLen++] = tmp3 >> 24; - partsData[partsDataLen++] = tmp4 >> 16; - partsData[partsDataLen++] = tmp4 >> 24; - } - } - - //Write the field descriptor - partsData[fieldDescLoc] = fieldDesc; - partsData[fieldDescLoc+1] = fieldDesc>>8; - if (fieldDesc & (1 << 15)) - { - partsData[fieldDesc3Loc] = fieldDesc>>16; - } - - if (particles[i].type == PT_SOAP) - soapCount++; - - if (particles[i].type == PT_RPEL && particles[i].ctype) - { - RESTRICTVERSION(91, 4); - } - else if (particles[i].type == PT_NWHL && particles[i].tmp) - { - RESTRICTVERSION(91, 5); - } - if (particles[i].type == PT_HEAC || particles[i].type == PT_SAWD || particles[i].type == PT_POLO - || particles[i].type == PT_RFRG || particles[i].type == PT_RFGL || particles[i].type == PT_LSNS) - { - RESTRICTVERSION(92, 0); - } - else if ((particles[i].type == PT_FRAY || particles[i].type == PT_INVIS) && particles[i].tmp) - { - RESTRICTVERSION(92, 0); - } - else if (particles[i].type == PT_PIPE || particles[i].type == PT_PPIP) - { - RESTRICTVERSION(93, 0); - } - if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS - || particles[i].type == PT_HSWC || particles[i].type == PT_PUMP) - { - if (particles[i].tmp == 1) - { - RESTRICTVERSION(93, 0); - } - } - if (PMAPBITS > 8) - { - if (TypeInCtype(particles[i].type, particles[i].ctype) && particles[i].ctype > 0xFF) - { - RESTRICTVERSION(93, 0); - } - else if (TypeInTmp(particles[i].type) && particles[i].tmp > 0xFF) - { - RESTRICTVERSION(93, 0); - } - else if (TypeInTmp2(particles[i].type, particles[i].tmp2) && particles[i].tmp2 > 0xFF) - { - RESTRICTVERSION(93, 0); - } - } - if (particles[i].type == PT_LDTC) + } + if (particles[i].type == PT_LDTC) + { + RESTRICTVERSION(94, 0); + } + if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS) + { + if (particles[i].tmp == 2) { RESTRICTVERSION(94, 0); } - if (particles[i].type == PT_TSNS || particles[i].type == PT_PSNS) - { - if (particles[i].tmp == 2) - { - RESTRICTVERSION(94, 0); - } - } - if (particles[i].type == PT_LSNS) - { - if (particles[i].tmp >= 1 || particles[i].tmp <= 3) - { - RESTRICTVERSION(95, 0); - } - } - if (particles[i].type == PT_LIFE) - { - RESTRICTVERSION(96, 0); - } - if (particles[i].type == PT_GLAS && particles[i].life > 0) - { - RESTRICTVERSION(97, 0); - } - if (PressureInTmp3(particles[i].type)) - { - RESTRICTVERSION(97, 0); - } - if (particles[i].type == PT_CONV && particles[i].tmp2 != 0) - { - RESTRICTVERSION(97, 0); - } - - //Get the pmap entry for the next particle in the same position - i = partsPosLink[i]; } + if (particles[i].type == PT_LSNS) + { + if (particles[i].tmp >= 1 || particles[i].tmp <= 3) + { + RESTRICTVERSION(95, 0); + } + } + if (particles[i].type == PT_LIFE) + { + RESTRICTVERSION(96, 0); + } + if (particles[i].type == PT_GLAS && particles[i].life > 0) + { + RESTRICTVERSION(97, 0); + } + if (PressureInTmp3(particles[i].type)) + { + RESTRICTVERSION(97, 0); + } + if (particles[i].type == PT_CONV && particles[i].tmp2 != 0) + { + RESTRICTVERSION(97, 0); + } + + //Get the pmap entry for the next particle in the same position + i = partsPosLink[i]; } } @@ -2333,44 +2228,41 @@ std::pair> GameSave::serialiseOPS() const { //Iterate through particles in the same order that they were saved - for (y=0;y()) { - for (x=0;x>8; - //Loop while there is a pmap entry - while (i) + if (particles[i].type==PT_SOAP) { - //Turn pmap entry into a partsptr index - i = i>>8; + //Only save forward link for each particle, back links can be deduced from other forward links + //linkedIndex is index within saved particles + 1, 0 means not saved or no link - if (particles[i].type==PT_SOAP) + unsigned linkedIndex = 0; + if ((particles[i].ctype&2) && particles[i].tmp>=0 && particles[i].tmp=0 && particles[i].tmp>16; - soapLinkData[soapLinkDataLen++] = (linkedIndex&0x00FF00)>>8; - soapLinkData[soapLinkDataLen++] = (linkedIndex&0x0000FF); + linkedIndex = partsSaveIndex[particles[i].tmp]; } - - //Get the pmap entry for the next particle in the same position - i = partsPosLink[i]; + soapLinkData[soapLinkDataLen++] = (linkedIndex&0xFF0000)>>16; + soapLinkData[soapLinkDataLen++] = (linkedIndex&0x00FF00)>>8; + soapLinkData[soapLinkDataLen++] = (linkedIndex&0x0000FF); } + + //Get the pmap entry for the next particle in the same position + i = partsPosLink[i]; } } } for (size_t i = 0; i < signs.size(); i++) { - if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH) + if(signs[i].text.length() && partS.OriginRect().Contains({ signs[i].x, signs[i].y })) { int x, y, w, h; bool v95 = false; @@ -2382,12 +2274,8 @@ std::pair> GameSave::serialiseOPS() const } } - bool fakeFromNewerVersion = false; -#if defined(SNAPSHOT) || defined(BETA) || defined(DEBUG) || MOD_ID > 0 // Mark save as incompatible with latest release - if (minimumMajorVersion > SAVE_VERSION || (minimumMajorVersion == SAVE_VERSION && minimumMinorVersion > MINOR_VERSION)) - fakeFromNewerVersion = true; -#endif + bool fakeFromNewerVersion = ALLOW_FAKE_NEWER_VERSION && (minimumMajorVersion > SAVE_VERSION || (minimumMajorVersion == SAVE_VERSION && minimumMinorVersion > MINOR_VERSION)); bson b; b.data = NULL; @@ -2403,9 +2291,8 @@ std::pair> GameSave::serialiseOPS() const bson_append_int(&b, "buildNum", BUILD_NUM); bson_append_int(&b, "snapshotId", SNAPSHOT_ID); bson_append_int(&b, "modId", MOD_ID); - bson_append_string(&b, "releaseType", IDENT_RELTYPE); + bson_append_string(&b, "releaseType", ByteString(1, IDENT_RELTYPE).c_str()); bson_append_string(&b, "platform", IDENT_PLATFORM); - bson_append_string(&b, "builtType", IDENT_BUILD); bson_append_string(&b, "ident", IDENT); bson_append_finish_object(&b); if (gravityMode == 3) @@ -2481,7 +2368,7 @@ std::pair> GameSave::serialiseOPS() const bson_append_binary(&b, "partsPos", (char)BSON_BIN_USER, (const char *)&partsPosData[0], partsPosDataLen); } if (hasWallData) - bson_append_binary(&b, "wallMap", (char)BSON_BIN_USER, (const char *)&wallData[0], wallDataLen); + bson_append_binary(&b, "wallMap", (char)BSON_BIN_USER, (const char *)wallData.data(), wallDataLen); if (fanDataLen) bson_append_binary(&b, "fanMap", (char)BSON_BIN_USER, (const char *)&fanData[0], fanDataLen); if (hasPressure && pressDataLen) @@ -2494,10 +2381,18 @@ std::pair> GameSave::serialiseOPS() const bson_append_binary(&b, "ambientMap", (char)BSON_BIN_USER, (const char*)&ambientData[0], ambientDataLen); if (soapLinkDataLen) bson_append_binary(&b, "soapLinks", (char)BSON_BIN_USER, (const char *)&soapLinkData[0], soapLinkDataLen); + if (ensureDeterminism) + { + bson_append_bool(&b, "ensureDeterminism", ensureDeterminism); + bson_append_binary(&b, "blockAir", (char)BSON_BIN_USER, (const char *)blockAirData.data(), blockAirData.Size().X * blockAirData.Size().Y); + bson_append_long(&b, "frameCount", int64_t(frameCount)); + bson_append_binary(&b, "rngState", (char)BSON_BIN_USER, (const char *)&rngState, sizeof(rngState)); + RESTRICTVERSION(98, 0); + } unsigned int signsCount = 0; for (size_t i = 0; i < signs.size(); i++) { - if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH) + if(signs[i].text.length() && partS.OriginRect().Contains({ signs[i].x, signs[i].y })) { signsCount++; } @@ -2507,7 +2402,7 @@ std::pair> GameSave::serialiseOPS() const bson_append_start_array(&b, "signs"); for (size_t i = 0; i < signs.size(); i++) { - if(signs[i].text.length() && signs[i].x>=0 && signs[i].x<=fullW && signs[i].y>=0 && signs[i].y<=fullH) + if(signs[i].text.length() && partS.OriginRect().Contains({ signs[i].x, signs[i].y })) { bson_append_start_object(&b, "sign"); bson_append_string(&b, "text", signs[i].text.ToUtf8().c_str()); @@ -2541,9 +2436,10 @@ std::pair> GameSave::serialiseOPS() const } auto compressedSize = int(outputData.size()); -#ifdef DEBUG - printf("compressed data: %d\n", compressedSize); -#endif + if constexpr (DEBUG) + { + printf("compressed data: %d\n", compressedSize); + } outputData.resize(compressedSize + 12); auto header = (unsigned char *)&outputData[compressedSize]; @@ -2553,8 +2449,8 @@ std::pair> GameSave::serialiseOPS() const header[3] = '1'; header[4] = SAVE_VERSION; header[5] = CELL; - header[6] = blockW; - header[7] = blockH; + header[6] = blockS.X; + header[7] = blockS.Y; header[8] = finalDataLen; header[9] = finalDataLen >> 8; header[10] = finalDataLen >> 16; @@ -2688,26 +2584,6 @@ static void ConvertJsonToBson(bson *b, Json::Value j, int depth) } } -bool GameSave::TypeInCtype(int type, int ctype) -{ - return ctype >= 0 && ctype < PT_NUM && - (type == PT_CLNE || type == PT_PCLN || type == PT_BCLN || type == PT_PBCN || - type == PT_STOR || type == PT_CONV || type == PT_STKM || type == PT_STKM2 || - type == PT_FIGH || type == PT_LAVA || type == PT_SPRK || type == PT_PSTN || - type == PT_CRAY || type == PT_DTEC || type == PT_DRAY || type == PT_PIPE || - type == PT_PPIP || type == PT_LDTC); -} - -bool GameSave::TypeInTmp(int type) -{ - return type == PT_STOR; -} - -bool GameSave::TypeInTmp2(int type, int tmp2) -{ - return (type == PT_VIRS || type == PT_VRSG || type == PT_VRSS) && (tmp2 >= 0 && tmp2 < PT_NUM); -} - bool GameSave::PressureInTmp3(int type) { return type == PT_QRTZ || type == PT_GLAS || type == PT_TUNG; diff --git a/src/client/GameSave.h b/src/client/GameSave.h index 7ff3d64f1..2490372f0 100644 --- a/src/client/GameSave.h +++ b/src/client/GameSave.h @@ -1,12 +1,13 @@ #pragma once -#include "Config.h" - -#include +#include "common/Plane.h" #include "common/String.h" +#include "common/tpt-rand.h" #include "simulation/Sign.h" #include "simulation/Particle.h" #include "Misc.h" - +#include "SimulationConfig.h" +#include +#include #include struct sign; @@ -53,57 +54,38 @@ public: } }; -template -struct Plane -{ - int width = 0; - int height = 0; - std::vector items; - // invariant: items.size() == width * height - - Item *operator [](int y) - { - return &items[y * width]; - } - - const Item *operator [](int y) const - { - return &items[y * width]; - } - - Plane() = default; - Plane(int newWidth, int newHeight, Item defaultVal) : width(newWidth), height(newHeight), items(width * height, defaultVal) - { - } -}; - class GameSave { // number of pixels translated. When translating CELL pixels, shift all CELL grids - vector2d translated = { 0, 0 }; void readOPS(const std::vector &data); void readPSv(const std::vector &data); std::pair> serialiseOPS() const; public: - int blockWidth = 0; - int blockHeight = 0; + Vec2 blockSize = { 0, 0 }; bool fromNewerVersion = false; int majorVersion = 0; int minorVersion = 0; bool hasPressure = false; bool hasAmbientHeat = false; + bool hasBlockAirMaps = false; // only written by readOPS, never read + bool ensureDeterminism = false; // only taken seriously by serializeOPS; readOPS may set this even if the save does not have everything required for determinism + bool hasRngState = false; // only written by readOPS, never read + RNG::State rngState; + uint64_t frameCount = 0; //Simulation data int particlesCount = 0; std::vector particles; - Plane blockMap; - Plane fanVelX; - Plane fanVelY; - Plane pressure; - Plane velocityX; - Plane velocityY; - Plane ambientHeat; + PlaneAdapter> blockMap; + PlaneAdapter> fanVelX; + PlaneAdapter> fanVelY; + PlaneAdapter> pressure; + PlaneAdapter> velocityX; + PlaneAdapter> velocityY; + PlaneAdapter> ambientHeat; + PlaneAdapter> blockAir; + PlaneAdapter> blockAirh; //Simulation Options bool waterEEnabled = false; @@ -117,6 +99,7 @@ public: int airMode = 0; float ambientAirTemp = R_TEMP + 273.15f; int edgeMode = 0; + bool wantAuthors = true; //Signs std::vector signs; @@ -131,20 +114,15 @@ public: int pmapbits = 8; // default to 8 bits for older saves - GameSave(int width, int height); - GameSave(const std::vector &data); - void setSize(int width, int height); + GameSave(Vec2 newBlockSize); + GameSave(const std::vector &data, bool newWantAuthors = true); + void setSize(Vec2 newBlockSize); // return value is [ fakeFromNewerVersion, gameData ] std::pair> Serialise() const; - vector2d Translate(vector2d translate); - void Transform(matrix2d transform, vector2d translate); - void Transform(matrix2d transform, vector2d translate, vector2d translateReal, int newWidth, int newHeight); + void Transform(Mat2 transform, Vec2 nudge); void Expand(const std::vector &data); - static bool TypeInCtype(int type, int ctype); - static bool TypeInTmp(int type); - static bool TypeInTmp2(int type, int tmp2); static bool PressureInTmp3(int type); GameSave& operator << (Particle &v); diff --git a/src/client/LoginInfo.h b/src/client/LoginInfo.h new file mode 100644 index 000000000..b28169545 --- /dev/null +++ b/src/client/LoginInfo.h @@ -0,0 +1,10 @@ +#pragma once +#include "User.h" +#include "ServerNotification.h" +#include + +struct LoginInfo +{ + User user; + std::vector notifications; +}; diff --git a/src/client/MD5.cpp b/src/client/MD5.cpp index b85b4c828..809de1b8e 100644 --- a/src/client/MD5.cpp +++ b/src/client/MD5.cpp @@ -1,6 +1,6 @@ // based on public-domain code from Colin Plumb (1993) -#include #include "MD5.h" +#include static unsigned getu32(const unsigned char *addr) { diff --git a/src/client/MD5.h b/src/client/MD5.h index 05d129394..5d6926b2e 100644 --- a/src/client/MD5.h +++ b/src/client/MD5.h @@ -1,6 +1,4 @@ -#ifndef MD5_H -#define MD5_H -#include "Config.h" +#pragma once struct md5_context { @@ -15,5 +13,3 @@ 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); - -#endif diff --git a/src/client/SaveFile.cpp b/src/client/SaveFile.cpp index ba4622638..c2ed54fed 100644 --- a/src/client/SaveFile.cpp +++ b/src/client/SaveFile.cpp @@ -1,20 +1,8 @@ #include "SaveFile.h" #include "GameSave.h" -#include "common/Platform.h" - -SaveFile::SaveFile(SaveFile & save): - gameSave(NULL), - filename(save.filename), - displayName(save.displayName), - loadingError(save.loadingError), - lazyLoad(save.lazyLoad) -{ - if (save.gameSave) - gameSave = new GameSave(*save.gameSave); -} +#include "common/platform/Platform.h" SaveFile::SaveFile(ByteString filename, bool newLazyLoad): - gameSave(NULL), filename(filename), displayName(filename.FromUtf8()), loadingError(""), @@ -23,7 +11,7 @@ SaveFile::SaveFile(ByteString filename, bool newLazyLoad): } -GameSave * SaveFile::GetGameSave() +const GameSave *SaveFile::LazyGetGameSave() // non-owning { if (!gameSave && !loadingError.size() && lazyLoad) { @@ -32,7 +20,7 @@ GameSave * SaveFile::GetGameSave() std::vector data; if (Platform::ReadFile(data, filename)) { - gameSave = new GameSave(std::move(data)); + gameSave = std::make_unique(std::move(data)); } else { @@ -44,24 +32,33 @@ GameSave * SaveFile::GetGameSave() loadingError = ByteString(e.what()).FromUtf8(); } } - return gameSave; + return gameSave.get(); +} + +const GameSave *SaveFile::GetGameSave() const +{ + return gameSave.get(); +} + +std::unique_ptr SaveFile::TakeGameSave() +{ + return std::move(gameSave); } void SaveFile::LazyUnload() { - if (lazyLoad && gameSave) + if (lazyLoad) { - delete gameSave; - gameSave = nullptr; + gameSave.reset(); } } -void SaveFile::SetGameSave(GameSave * save) +void SaveFile::SetGameSave(std::unique_ptr newGameSave) { - gameSave = save; + gameSave = std::move(newGameSave); } -ByteString SaveFile::GetName() +const ByteString &SaveFile::GetName() const { return filename; } @@ -71,7 +68,7 @@ void SaveFile::SetFileName(ByteString fileName) this->filename = fileName; } -String SaveFile::GetDisplayName() +const String &SaveFile::GetDisplayName() const { return displayName; } @@ -81,7 +78,7 @@ void SaveFile::SetDisplayName(String displayName) this->displayName = displayName; } -String SaveFile::GetError() +const String &SaveFile::GetError() const { return loadingError; } @@ -90,10 +87,3 @@ void SaveFile::SetLoadingError(String error) { loadingError = error; } - -SaveFile::~SaveFile() { - if (gameSave) - { - delete gameSave; - } -} diff --git a/src/client/SaveFile.h b/src/client/SaveFile.h index b2b62362d..b09ba143c 100644 --- a/src/client/SaveFile.h +++ b/src/client/SaveFile.h @@ -1,33 +1,29 @@ -#ifndef SAVEFILE_H_ -#define SAVEFILE_H_ - +#pragma once #include "common/String.h" +#include class GameSave; class SaveFile { public: - SaveFile(SaveFile & save); SaveFile(ByteString filename, bool newLazyLoad = false); - GameSave * GetGameSave(); - void SetGameSave(GameSave * save); - String GetDisplayName(); + const GameSave *LazyGetGameSave(); + const GameSave *GetGameSave() const; + std::unique_ptr TakeGameSave(); + void SetGameSave(std::unique_ptr newSameSave); + const String &GetDisplayName() const; void SetDisplayName(String displayName); - ByteString GetName(); + const ByteString &GetName() const; void SetFileName(ByteString fileName); - String GetError(); + const String &GetError() const; void SetLoadingError(String error); void LazyUnload(); - - virtual ~SaveFile(); private: - GameSave * gameSave; + std::unique_ptr gameSave; ByteString filename; String displayName; String loadingError; bool lazyLoad; }; - -#endif /* SAVEFILE_H_ */ diff --git a/src/client/SaveInfo.cpp b/src/client/SaveInfo.cpp index bfa9fb439..0e0c08634 100644 --- a/src/client/SaveInfo.cpp +++ b/src/client/SaveInfo.cpp @@ -1,31 +1,7 @@ #include "SaveInfo.h" #include "GameSave.h" -SaveInfo::SaveInfo(SaveInfo & save): - id(save.id), - createdDate(save.createdDate), - updatedDate(save.updatedDate), - votesUp(save.votesUp), - votesDown(save.votesDown), - vote(save.vote), - Favourite(false), - Comments(save.Comments), - Views(save.Views), - Version(save.Version), - userName(save.userName), - name(save.name), - Description(save.Description), - Published(save.Published), - gameSave(NULL) -{ - std::list tagsSorted = save.tags; - tagsSorted.sort(); - tags = tagsSorted; - if (save.gameSave) - gameSave = new GameSave(*save.gameSave); -} - -SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name): +SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name): id(_id), createdDate(_createdDate), updatedDate(_updatedDate), @@ -39,14 +15,12 @@ SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, in userName(_userName), name(_name), Description(""), - Published(false), - tags(), - gameSave(NULL) + Published(false) { } -SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list tags_): +SaveInfo::SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list tags_): id(_id), createdDate(_createdDate), updatedDate(_updatedDate), @@ -60,28 +34,18 @@ SaveInfo::SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, in userName(_userName), name(_name), Description(description_), - Published(published_), - tags(), - gameSave(NULL) + Published(published_) { std::list tagsSorted = tags_; tagsSorted.sort(); tags=tagsSorted; } -SaveInfo::~SaveInfo() -{ - if(gameSave) - { - delete gameSave; - } -} - void SaveInfo::SetName(String name) { this->name = name; } -String SaveInfo::GetName() +const String &SaveInfo::GetName() const { return name; } @@ -90,7 +54,7 @@ void SaveInfo::SetDescription(String description) { Description = description; } -String SaveInfo::GetDescription() +const String &SaveInfo::GetDescription() const { return Description; } @@ -99,7 +63,7 @@ void SaveInfo::SetPublished(bool published) { Published = published; } -bool SaveInfo::GetPublished() +bool SaveInfo::GetPublished() const { return Published; } @@ -108,7 +72,7 @@ void SaveInfo::SetVote(int vote) { this->vote = vote; } -int SaveInfo::GetVote() +int SaveInfo::GetVote() const { return vote; } @@ -118,7 +82,7 @@ void SaveInfo::SetUserName(ByteString userName) this->userName = userName; } -ByteString SaveInfo::GetUserName() +const ByteString &SaveInfo::GetUserName() const { return userName; } @@ -127,7 +91,7 @@ void SaveInfo::SetID(int id) { this->id = id; } -int SaveInfo::GetID() +int SaveInfo::GetID() const { return id; } @@ -136,7 +100,7 @@ void SaveInfo::SetVotesUp(int votesUp) { this->votesUp = votesUp; } -int SaveInfo::GetVotesUp() +int SaveInfo::GetVotesUp() const { return votesUp; } @@ -145,7 +109,7 @@ void SaveInfo::SetVotesDown(int votesDown) { this->votesDown = votesDown; } -int SaveInfo::GetVotesDown() +int SaveInfo::GetVotesDown() const { return votesDown; } @@ -154,7 +118,7 @@ void SaveInfo::SetVersion(int version) { this->Version = version; } -int SaveInfo::GetVersion() +int SaveInfo::GetVersion() const { return Version; } @@ -166,18 +130,33 @@ void SaveInfo::SetTags(std::list tags) this->tags=tagsSorted; } -std::list SaveInfo::GetTags() +std::list SaveInfo::GetTags() const { return tags; } -GameSave * SaveInfo::GetGameSave() +const GameSave *SaveInfo::GetGameSave() const { - return gameSave; + return gameSave.get(); } -void SaveInfo::SetGameSave(GameSave * saveGame) +std::unique_ptr SaveInfo::TakeGameSave() { - delete gameSave; - gameSave = saveGame; + return std::move(gameSave); +} + +void SaveInfo::SetGameSave(std::unique_ptr newGameSave) +{ + gameSave = std::move(newGameSave); +} + +std::unique_ptr SaveInfo::CloneInfo() const +{ + auto clone = std::make_unique(id, createdDate, updatedDate, votesUp, votesDown, vote, userName, name, Description, Published, tags); + clone->Favourite = false; + clone->Comments = Comments; + clone->Views = Views; + clone->Version = Version; + clone->tags.sort(); + return clone; } diff --git a/src/client/SaveInfo.h b/src/client/SaveInfo.h index 9e32226da..a5fc96e2d 100644 --- a/src/client/SaveInfo.h +++ b/src/client/SaveInfo.h @@ -1,9 +1,8 @@ -#ifndef SAVE_H -#define SAVE_H -#include "Config.h" - -#include +#pragma once #include "common/String.h" +#include +#include +#include #ifdef GetUserName # undef GetUserName // dammit windows @@ -16,8 +15,8 @@ class SaveInfo private: public: int id; - int createdDate; - int updatedDate; + time_t createdDate; + time_t updatedDate; int votesUp, votesDown; int vote; bool Favourite; @@ -32,48 +31,45 @@ public: bool Published; std::list tags; - GameSave * gameSave; + std::unique_ptr gameSave; - SaveInfo(SaveInfo & save); + SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name); - SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, ByteString _userName, String _name); - - SaveInfo(int _id, int _createdDate, int _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list tags); - - ~SaveInfo(); + SaveInfo(int _id, time_t _createdDate, time_t _updatedDate, int _votesUp, int _votesDown, int _vote, ByteString _userName, String _name, String description_, bool published_, std::list tags); void SetName(String name); - String GetName(); + const String &GetName() const; void SetDescription(String description); - String GetDescription(); + const String &GetDescription() const; void SetPublished(bool published); - bool GetPublished(); + bool GetPublished() const; void SetUserName(ByteString userName); - ByteString GetUserName(); + const ByteString &GetUserName() const; void SetID(int id); - int GetID(); + int GetID() const; void SetVote(int vote); - int GetVote(); + int GetVote() const; void SetVotesUp(int votesUp); - int GetVotesUp(); + int GetVotesUp() const; void SetVotesDown(int votesDown); - int GetVotesDown(); + int GetVotesDown() const; void SetVersion(int version); - int GetVersion(); + int GetVersion() const; void SetTags(std::list tags); - std::list GetTags(); + std::list GetTags() const; - GameSave * GetGameSave(); - void SetGameSave(GameSave * gameSave); + const GameSave *GetGameSave() const; + std::unique_ptr TakeGameSave(); + void SetGameSave(std::unique_ptr newGameSave); + + std::unique_ptr CloneInfo() const; }; - -#endif // SAVE_H diff --git a/src/client/Search.h b/src/client/Search.h new file mode 100644 index 000000000..4077e5c13 --- /dev/null +++ b/src/client/Search.h @@ -0,0 +1,17 @@ +#pragma once + +namespace http +{ + enum Category + { + categoryNone, + categoryMyOwn, + categoryFavourites, + }; + + enum Sort + { + sortByVotes, + sortByDate, + }; +} diff --git a/src/client/ServerNotification.h b/src/client/ServerNotification.h new file mode 100644 index 000000000..cd3c027be --- /dev/null +++ b/src/client/ServerNotification.h @@ -0,0 +1,8 @@ +#pragma once +#include "common/String.h" + +struct ServerNotification +{ + String text; + ByteString link; +}; diff --git a/src/client/StartupInfo.h b/src/client/StartupInfo.h new file mode 100644 index 000000000..f1a27baa3 --- /dev/null +++ b/src/client/StartupInfo.h @@ -0,0 +1,29 @@ +#pragma once +#include "common/String.h" +#include "ServerNotification.h" +#include +#include + +struct UpdateInfo +{ + enum Channel + { + channelStable, + channelBeta, + channelSnapshot, + }; + Channel channel; + ByteString file; + String changeLog; + int major = 0; + int minor = 0; + int build = 0; +}; + +struct StartupInfo +{ + bool sessionGood = false; + String messageOfTheDay; + std::vector notifications; + std::optional updateInfo; +}; diff --git a/src/client/ThumbnailRendererTask.cpp b/src/client/ThumbnailRendererTask.cpp index d0e629b72..6b6f22a30 100644 --- a/src/client/ThumbnailRendererTask.cpp +++ b/src/client/ThumbnailRendererTask.cpp @@ -13,13 +13,11 @@ int ThumbnailRendererTask::QueueSize() return queueSize; } -ThumbnailRendererTask::ThumbnailRendererTask(GameSave *save, int width, int height, bool autoRescale, bool decorations, bool fire) : - Save(new GameSave(*save)), - Width(width), - Height(height), - Decorations(decorations), - Fire(fire), - AutoRescale(autoRescale) +ThumbnailRendererTask::ThumbnailRendererTask(GameSave const &save, Vec2 size, bool decorations, bool fire): + save(std::make_unique(save)), + size(size), + decorations(decorations), + fire(fire) { queueSize += 1; } @@ -31,33 +29,11 @@ ThumbnailRendererTask::~ThumbnailRendererTask() bool ThumbnailRendererTask::doWork() { - thumbnail = std::unique_ptr(SaveRenderer::Ref().Render(Save.get(), Decorations, Fire)); + thumbnail = std::unique_ptr(SaveRenderer::Ref().Render(save.get(), decorations, fire)); if (thumbnail) { - if (AutoRescale) - { - int scaleX = (int)std::ceil((float)thumbnail->Width / Width); - int scaleY = (int)std::ceil((float)thumbnail->Height / Height); - int scale = scaleX > scaleY ? scaleX : scaleY; - int newWidth = thumbnail->Width / scale, newHeight = thumbnail->Height / scale; - thumbnail->Resize(newWidth, newHeight, true); - newWidth = thumbnail->Width; - newHeight = thumbnail->Height; - if (newWidth > Width || newHeight > Height) - { - auto croppedWidth = newWidth > Width ? Width : newWidth; - auto croppedHeight = newHeight > Height ? Height : newHeight; - thumbnail->Crop(croppedWidth, croppedHeight, (newWidth - croppedWidth) / 2, (newHeight - croppedHeight) / 2); - newWidth = thumbnail->Width; - newHeight = thumbnail->Height; - } - Width = newWidth; - Height = newHeight; - } - else - { - thumbnail->Resize(Width, Height, true); - } + thumbnail->ResizeToFit(size, true); + size = thumbnail->Size(); return true; } else diff --git a/src/client/ThumbnailRendererTask.h b/src/client/ThumbnailRendererTask.h index 6a1b4989f..8a57ef2f6 100644 --- a/src/client/ThumbnailRendererTask.h +++ b/src/client/ThumbnailRendererTask.h @@ -1,6 +1,5 @@ -#ifndef THUMBNAILRENDERER_H -#define THUMBNAILRENDERER_H - +#pragma once +#include "common/Vec2.h" #include "tasks/AbandonableTask.h" #include @@ -9,17 +8,16 @@ class GameSave; class VideoBuffer; class ThumbnailRendererTask : public AbandonableTask { - std::unique_ptr Save; - int Width, Height; - bool Decorations; - bool Fire; - bool AutoRescale; + std::unique_ptr save; + Vec2 size; + bool decorations; + bool fire; std::unique_ptr thumbnail; static int queueSize; public: - ThumbnailRendererTask(GameSave *save, int width, int height, bool autoRescale = false, bool decorations = true, bool fire = true); + ThumbnailRendererTask(GameSave const &, Vec2 size, bool decorations, bool fire); virtual ~ThumbnailRendererTask(); virtual bool doWork() override; @@ -27,6 +25,3 @@ public: static int QueueSize(); }; - -#endif // THUMBNAILRENDERER_H - diff --git a/src/client/User.cpp b/src/client/User.cpp new file mode 100644 index 000000000..49b3ac75c --- /dev/null +++ b/src/client/User.cpp @@ -0,0 +1,32 @@ +#include "User.h" + +static const std::vector> elevationStrings = { + { User::ElevationAdmin , "Admin" }, + { User::ElevationMod , "Mod" }, + { User::ElevationHalfMod, "HalfMod" }, + { User::ElevationNone , "None" }, +}; + +User::Elevation User::ElevationFromString(ByteString str) +{ + auto it = std::find_if(elevationStrings.begin(), elevationStrings.end(), [&str](auto &item) { + return item.second == str; + }); + if (it != elevationStrings.end()) + { + return it->first; + } + return ElevationNone; +} + +ByteString User::ElevationToString(Elevation elevation) +{ + auto it = std::find_if(elevationStrings.begin(), elevationStrings.end(), [elevation](auto &item) { + return item.first == elevation; + }); + if (it != elevationStrings.end()) + { + return it->second; + } + return "None"; +} diff --git a/src/client/User.h b/src/client/User.h index 3d9aab469..63c121cf3 100644 --- a/src/client/User.h +++ b/src/client/User.h @@ -1,6 +1,4 @@ -#ifndef USER_H_ -#define USER_H_ - +#pragma once #include "common/String.h" @@ -9,8 +7,14 @@ class User public: enum Elevation { - ElevationAdmin, ElevationModerator, ElevationNone + ElevationNone, + ElevationHalfMod, + ElevationMod, + ElevationAdmin, }; + static Elevation ElevationFromString(ByteString str); + static ByteString ElevationToString(Elevation elevation); + int UserID; ByteString Username; ByteString SessionID; @@ -27,5 +31,3 @@ public: } }; - -#endif /* USER_H_ */ diff --git a/src/client/UserInfo.h b/src/client/UserInfo.h index 080f95136..7d1e717eb 100644 --- a/src/client/UserInfo.h +++ b/src/client/UserInfo.h @@ -1,6 +1,4 @@ -#ifndef USERINFO_H_ -#define USERINFO_H_ - +#pragma once #include "common/String.h" class UserInfo @@ -37,6 +35,3 @@ public: { } UserInfo() {} }; - - -#endif /* USER_H_ */ diff --git a/src/client/http/APIRequest.cpp b/src/client/http/APIRequest.cpp index e304d0f27..f877a50b4 100644 --- a/src/client/http/APIRequest.cpp +++ b/src/client/http/APIRequest.cpp @@ -1,41 +1,36 @@ #include "APIRequest.h" - #include "client/Client.h" namespace http { - APIRequest::APIRequest(ByteString url) : Request(url) + APIRequest::APIRequest(ByteString url, AuthMode authMode, bool newCheckStatus) : Request(url), checkStatus(newCheckStatus) { - User user = Client::Ref().GetAuthUser(); - AuthHeaders(ByteString::Build(user.UserID), user.SessionID); + auto user = Client::Ref().GetAuthUser(); + if (authMode == authRequire && !user.UserID) + { + FailEarly("Not authenticated"); + return; + } + if (authMode != authOmit && user.UserID) + { + AuthHeaders(ByteString::Build(user.UserID), user.SessionID); + } } - APIRequest::~APIRequest() + Json::Value APIRequest::Finish() { - } - - APIRequest::Result APIRequest::Finish() - { - Result result; + auto [ status, data ] = Request::Finish(); + ParseResponse(data, status, checkStatus ? responseJson : responseData); + Json::Value document; try { - ByteString data = Request::Finish(&result.status); - // Note that at this point it's not safe to use any member of the - // APIRequest object as Request::Finish signals RequestManager - // to delete it. - Client::Ref().ParseServerReturn(data, result.status, true); - if (result.status == 200 && data.size()) - { - std::istringstream dataStream(data); - Json::Value objDocument; - dataStream >> objDocument; - result.document = std::unique_ptr(new Json::Value(objDocument)); - } + std::istringstream ss(data); + ss >> document; } - catch (std::exception & e) + catch (const std::exception &ex) { + throw RequestError("Could not read response: " + ByteString(ex.what())); } - return result; + return document; } } - diff --git a/src/client/http/APIRequest.h b/src/client/http/APIRequest.h index dcd529365..893030805 100644 --- a/src/client/http/APIRequest.h +++ b/src/client/http/APIRequest.h @@ -1,30 +1,23 @@ -#ifndef APIREQUEST2_H -#define APIREQUEST2_H - +#pragma once #include "Request.h" #include "common/String.h" #include -#include -#include - namespace http { class APIRequest : public Request { + bool checkStatus; + public: - struct Result + enum AuthMode { - int status; - std::unique_ptr document; + authRequire, + authUse, + authOmit, }; + APIRequest(ByteString url, AuthMode authMode, bool newCheckStatus); - APIRequest(ByteString url); - virtual ~APIRequest(); - - Result Finish(); + Json::Value Finish(); }; } - -#endif // APIREQUEST2_H - diff --git a/src/client/http/AddCommentRequest.cpp b/src/client/http/AddCommentRequest.cpp new file mode 100644 index 000000000..c5291eaf2 --- /dev/null +++ b/src/client/http/AddCommentRequest.cpp @@ -0,0 +1,21 @@ +#include "AddCommentRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + AddCommentRequest::AddCommentRequest(int saveID, String comment) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID), authRequire, true) + { + auto user = Client::Ref().GetAuthUser(); + AddPostData(FormData{ + { "Comment", comment.ToUtf8() }, + { "Key", user.SessionKey }, + }); + } + + void AddCommentRequest::Finish() + { + APIRequest::Finish(); + } +} diff --git a/src/client/http/AddCommentRequest.h b/src/client/http/AddCommentRequest.h new file mode 100644 index 000000000..e776663eb --- /dev/null +++ b/src/client/http/AddCommentRequest.h @@ -0,0 +1,13 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class AddCommentRequest : public APIRequest + { + public: + AddCommentRequest(int saveID, String comment); + + void Finish(); + }; +} diff --git a/src/client/http/AddTagRequest.cpp b/src/client/http/AddTagRequest.cpp new file mode 100644 index 000000000..58cdbe643 --- /dev/null +++ b/src/client/http/AddTagRequest.cpp @@ -0,0 +1,29 @@ +#include "AddTagRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + AddTagRequest::AddTagRequest(int saveID, ByteString tag) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=add&ID=", saveID, "&Tag=", tag, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true) + { + } + + std::list AddTagRequest::Finish() + { + auto result = APIRequest::Finish(); + std::list tags; + try + { + for (auto &tag : result["Tags"]) + { + tags.push_back(tag.asString()); + } + } + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return tags; + } +} diff --git a/src/client/http/AddTagRequest.h b/src/client/http/AddTagRequest.h new file mode 100644 index 000000000..52f066289 --- /dev/null +++ b/src/client/http/AddTagRequest.h @@ -0,0 +1,14 @@ +#pragma once +#include "APIRequest.h" +#include + +namespace http +{ + class AddTagRequest : public APIRequest + { + public: + AddTagRequest(int saveID, ByteString tag); + + std::list Finish(); + }; +} diff --git a/src/client/http/DeleteSaveRequest.cpp b/src/client/http/DeleteSaveRequest.cpp new file mode 100644 index 000000000..58f7eb2e7 --- /dev/null +++ b/src/client/http/DeleteSaveRequest.cpp @@ -0,0 +1,16 @@ +#include "DeleteSaveRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + DeleteSaveRequest::DeleteSaveRequest(int saveID) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Delete&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true) + { + } + + void DeleteSaveRequest::Finish() + { + APIRequest::Finish(); + } +} diff --git a/src/client/http/DeleteSaveRequest.h b/src/client/http/DeleteSaveRequest.h new file mode 100644 index 000000000..def0c6560 --- /dev/null +++ b/src/client/http/DeleteSaveRequest.h @@ -0,0 +1,13 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class DeleteSaveRequest : public APIRequest + { + public: + DeleteSaveRequest(int saveID); + + void Finish(); + }; +} diff --git a/src/client/http/ExecVoteRequest.cpp b/src/client/http/ExecVoteRequest.cpp new file mode 100644 index 000000000..0659eb911 --- /dev/null +++ b/src/client/http/ExecVoteRequest.cpp @@ -0,0 +1,23 @@ +#include "ExecVoteRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + ExecVoteRequest::ExecVoteRequest(int saveID, int newDirection) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Vote.api"), authRequire, false), + direction(newDirection) + { + AddPostData(FormData{ + { "ID", ByteString::Build(saveID) }, + { "Action", direction ? (direction == 1 ? "Up" : "Down") : "Reset" }, + { "Key", Client::Ref().GetAuthUser().SessionKey }, + }); + } + + void ExecVoteRequest::Finish() + { + auto [ status, data ] = Request::Finish(); + ParseResponse(data, status, responseOk); + } +} diff --git a/src/client/http/ExecVoteRequest.h b/src/client/http/ExecVoteRequest.h new file mode 100644 index 000000000..99df9da44 --- /dev/null +++ b/src/client/http/ExecVoteRequest.h @@ -0,0 +1,20 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class ExecVoteRequest : public APIRequest + { + int direction; + + public: + ExecVoteRequest(int saveID, int newDirection); + + void Finish(); + + int Direction() const + { + return direction; + } + }; +} diff --git a/src/client/http/FavouriteSaveRequest.cpp b/src/client/http/FavouriteSaveRequest.cpp new file mode 100644 index 000000000..48ee3b2f0 --- /dev/null +++ b/src/client/http/FavouriteSaveRequest.cpp @@ -0,0 +1,28 @@ +#include "FavouriteSaveRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + static ByteString Url(int saveID, bool favourite) + { + ByteStringBuilder builder; + builder << SCHEME << SERVER << "/Browse/Favourite.json?ID=" << saveID << "&Key=" << Client::Ref().GetAuthUser().SessionKey; + if (!favourite) + { + builder << "&Mode=Remove"; + } + return builder.Build(); + } + + FavouriteSaveRequest::FavouriteSaveRequest(int saveID, bool newFavourite) : + APIRequest(Url(saveID, newFavourite), authRequire, true), + favourite(newFavourite) + { + } + + void FavouriteSaveRequest::Finish() + { + APIRequest::Finish(); + } +} diff --git a/src/client/http/FavouriteSaveRequest.h b/src/client/http/FavouriteSaveRequest.h new file mode 100644 index 000000000..f4d561ff0 --- /dev/null +++ b/src/client/http/FavouriteSaveRequest.h @@ -0,0 +1,20 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class FavouriteSaveRequest : public APIRequest + { + bool favourite; + + public: + FavouriteSaveRequest(int saveID, bool newFavourite); + + void Finish(); + + bool Favourite() const + { + return favourite; + } + }; +} diff --git a/src/client/http/GetCommentsRequest.cpp b/src/client/http/GetCommentsRequest.cpp new file mode 100644 index 000000000..4207555ef --- /dev/null +++ b/src/client/http/GetCommentsRequest.cpp @@ -0,0 +1,36 @@ +#include "GetCommentsRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + GetCommentsRequest::GetCommentsRequest(int saveID, int start, int count) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", start, "&Count=", count), authOmit, false) + { + } + + std::vector GetCommentsRequest::Finish() + { + auto result = APIRequest::Finish(); + std::vector comments; + auto user = Client::Ref().GetAuthUser(); + try + { + for (auto &comment : result) + { + comments.push_back({ + comment["Username"].asString(), + User::ElevationFromString(comment["Elevation"].asString()), + ByteString(comment["UserID"].asString()).ToNumber() == user.UserID, + comment["IsBanned"].asBool(), + ByteString(comment["Text"].asString()).FromUtf8(), + }); + } + } + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return comments; + } +} diff --git a/src/client/http/GetCommentsRequest.h b/src/client/http/GetCommentsRequest.h new file mode 100644 index 000000000..15bd65645 --- /dev/null +++ b/src/client/http/GetCommentsRequest.h @@ -0,0 +1,14 @@ +#pragma once +#include "APIRequest.h" +#include "client/Comment.h" + +namespace http +{ + class GetCommentsRequest : public APIRequest + { + public: + GetCommentsRequest(int saveID, int start, int count); + + std::vector Finish(); + }; +} diff --git a/src/client/http/GetSaveDataRequest.cpp b/src/client/http/GetSaveDataRequest.cpp new file mode 100644 index 000000000..e70c8a87a --- /dev/null +++ b/src/client/http/GetSaveDataRequest.cpp @@ -0,0 +1,28 @@ +#include "GetSaveDataRequest.h" +#include "Config.h" + +namespace http +{ + static ByteString Url(int saveID, int saveDate) + { + ByteStringBuilder builder; + builder << STATICSCHEME << STATICSERVER << "/" << saveID; + if (saveDate) + { + builder << "_" << saveDate; + } + builder << ".cps"; + return builder.Build(); + } + + GetSaveDataRequest::GetSaveDataRequest(int saveID, int saveDate) : Request(Url(saveID, saveDate)) + { + } + + std::vector GetSaveDataRequest::Finish() + { + auto [ status, data ] = Request::Finish(); + ParseResponse(data, status, responseData); + return std::vector(data.begin(), data.end()); + } +} diff --git a/src/client/http/GetSaveDataRequest.h b/src/client/http/GetSaveDataRequest.h new file mode 100644 index 000000000..50e19fe8d --- /dev/null +++ b/src/client/http/GetSaveDataRequest.h @@ -0,0 +1,13 @@ +#pragma once +#include "Request.h" + +namespace http +{ + class GetSaveDataRequest : public Request + { + public: + GetSaveDataRequest(int saveID, int saveDate); + + std::vector Finish(); + }; +} diff --git a/src/client/http/GetSaveRequest.cpp b/src/client/http/GetSaveRequest.cpp new file mode 100644 index 000000000..2e8e08565 --- /dev/null +++ b/src/client/http/GetSaveRequest.cpp @@ -0,0 +1,69 @@ +#include "GetSaveRequest.h" +#include "client/Client.h" +#include "client/SaveInfo.h" +#include "client/GameSave.h" +#include "Config.h" + +namespace http +{ + static ByteString Url(int saveID, int saveDate) + { + ByteStringBuilder builder; + builder << SCHEME << SERVER << "/Browse/View.json?ID=" << saveID; + if (saveDate) + { + builder << "&Date=" << saveDate; + } + return builder.Build(); + } + + GetSaveRequest::GetSaveRequest(int saveID, int saveDate) : Request(Url(saveID, saveDate)) + { + auto user = Client::Ref().GetAuthUser(); + if (user.UserID) + { + // This is needed so we know how we rated this save. + AuthHeaders(ByteString::Build(user.UserID), user.SessionID); + } + } + + std::unique_ptr GetSaveRequest::Finish() + { + auto [ status, data ] = Request::Finish(); + ParseResponse(data, status, responseData); + std::unique_ptr saveInfo; + try + { + Json::Value document; + std::istringstream ss(data); + ss >> document; + std::list tags; + for (auto &tag : document["Tags"]) + { + tags.push_back(tag.asString()); + } + saveInfo = std::make_unique( + document["ID"].asInt(), + document["DateCreated"].asInt64(), + document["Date"].asInt64(), + document["ScoreUp"].asInt(), + document["ScoreDown"].asInt(), + document["ScoreMine"].asInt(), + document["Username"].asString(), + ByteString(document["Name"].asString()).FromUtf8(), + ByteString(document["Description"].asString()).FromUtf8(), + document["Published"].asBool(), + tags + ); + saveInfo->Comments = document["Comments"].asInt(); + saveInfo->Favourite = document["Favourite"].asBool(); + saveInfo->Views = document["Views"].asInt(); + saveInfo->Version = document["Version"].asInt(); + } + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return saveInfo; + } +} diff --git a/src/client/http/GetSaveRequest.h b/src/client/http/GetSaveRequest.h new file mode 100644 index 000000000..3a2a42264 --- /dev/null +++ b/src/client/http/GetSaveRequest.h @@ -0,0 +1,16 @@ +#pragma once +#include "Request.h" +#include + +class SaveInfo; + +namespace http +{ + class GetSaveRequest : public Request + { + public: + GetSaveRequest(int saveID, int saveDate); + + std::unique_ptr Finish(); + }; +} diff --git a/src/client/http/GetUserInfoRequest.cpp b/src/client/http/GetUserInfoRequest.cpp index 64d8c5a8b..e8c004a33 100644 --- a/src/client/http/GetUserInfoRequest.cpp +++ b/src/client/http/GetUserInfoRequest.cpp @@ -1,30 +1,22 @@ #include "GetUserInfoRequest.h" - -#include "Config.h" #include "client/UserInfo.h" +#include "Config.h" namespace http { GetUserInfoRequest::GetUserInfoRequest(ByteString username) : - APIRequest(SCHEME SERVER "/User.json?Name=" + username) + APIRequest(ByteString::Build(SCHEME, SERVER, "/User.json?Name=", username), authOmit, false) { } - GetUserInfoRequest::~GetUserInfoRequest() + UserInfo GetUserInfoRequest::Finish() { - } - - std::unique_ptr GetUserInfoRequest::Finish() - { - std::unique_ptr user_info; auto result = APIRequest::Finish(); - // Note that at this point it's not safe to use any member of the - // GetUserInfoRequest object as Request::Finish signals RequestManager - // to delete it. - if (result.document) + UserInfo userInfo; + try { - auto &user = (*result.document)["User"]; - user_info = std::unique_ptr(new UserInfo( + auto &user = result["User"]; + userInfo = UserInfo( user["ID"].asInt(), user["Age"].asInt(), user["Username"].asString(), @@ -37,9 +29,13 @@ namespace http user["Forum"]["Topics"].asInt(), user["Forum"]["Replies"].asInt(), user["Forum"]["Reputation"].asInt() - )); + ); } - return user_info; + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return userInfo; } } diff --git a/src/client/http/GetUserInfoRequest.h b/src/client/http/GetUserInfoRequest.h index 9328bd185..ebe0d8c36 100644 --- a/src/client/http/GetUserInfoRequest.h +++ b/src/client/http/GetUserInfoRequest.h @@ -1,9 +1,6 @@ -#ifndef GETUSERINFOREQUEST2_H -#define GETUSERINFOREQUEST2_H - +#pragma once #include "APIRequest.h" - -class UserInfo; +#include "client/UserInfo.h" namespace http { @@ -11,11 +8,7 @@ namespace http { public: GetUserInfoRequest(ByteString username); - virtual ~GetUserInfoRequest(); - std::unique_ptr Finish(); + UserInfo Finish(); }; } - -#endif // GETUSERINFOREQUEST2_H - diff --git a/src/client/http/ImageRequest.cpp b/src/client/http/ImageRequest.cpp index 105884060..d30a80300 100644 --- a/src/client/http/ImageRequest.cpp +++ b/src/client/http/ImageRequest.cpp @@ -1,47 +1,27 @@ #include "ImageRequest.h" - -#include "common/Singleton.h" #include "graphics/Graphics.h" -#include "Config.h" - +#include "client/Client.h" #include namespace http { - ImageRequest::ImageRequest(ByteString url, int width, int height) : - Request(url), - Width(width), - Height(height) - { - } - - ImageRequest::~ImageRequest() + ImageRequest::ImageRequest(ByteString url, Vec2 newRequestedSize) : Request(url), requestedSize(newRequestedSize) { } std::unique_ptr ImageRequest::Finish() { - int width = Width; - int height = Height; - ByteString data = Request::Finish(nullptr); - // Note that at this point it's not safe to use any member of the - // ImageRequest object as Request::Finish signals RequestManager - // to delete it. - std::unique_ptr vb; - if (data.size()) + auto [ status, data ] = Request::Finish(); + ParseResponse(data, status, responseData); + auto vb = VideoBuffer::FromPNG(std::vector(data.begin(), data.end())); + if (vb) { - int imgw, imgh; - std::vector imageData; - if (PngDataToPixels(imageData, imgw, imgh, data.data(), data.size(), true)) - { - vb = std::unique_ptr(new VideoBuffer(imageData.data(), imgw, imgh)); - } - else - { - vb = std::unique_ptr(new VideoBuffer(32, 32)); - vb->SetCharacter(14, 14, 'x', 255, 255, 255, 255); - } - vb->Resize(width, height, true); + vb->Resize(requestedSize, true); + } + else + { + vb = std::make_unique(Vec2(15, 16)); + vb->BlendChar(Vec2(2, 4), 0xE06E, 0xFFFFFF_rgb .WithAlpha(0xFF)); } return vb; } diff --git a/src/client/http/ImageRequest.h b/src/client/http/ImageRequest.h index 1d855c7fb..ba5fc6ca3 100644 --- a/src/client/http/ImageRequest.h +++ b/src/client/http/ImageRequest.h @@ -1,9 +1,7 @@ -#ifndef IMAGEREQUEST2_H -#define IMAGEREQUEST2_H - -#include "Request.h" +#pragma once #include "common/String.h" - +#include "common/Vec2.h" +#include "Request.h" #include class VideoBuffer; @@ -12,15 +10,11 @@ namespace http { class ImageRequest : public Request { - int Width, Height; - + Vec2 requestedSize; + public: - ImageRequest(ByteString url, int width, int height); - virtual ~ImageRequest(); + ImageRequest(ByteString url, Vec2 newRequestedSize); std::unique_ptr Finish(); }; } - -#endif // IMAGEREQUEST2_H - diff --git a/src/client/http/LoginRequest.cpp b/src/client/http/LoginRequest.cpp new file mode 100644 index 000000000..612081ae3 --- /dev/null +++ b/src/client/http/LoginRequest.cpp @@ -0,0 +1,45 @@ +#include "LoginRequest.h" +#include "Config.h" +#include "client/Client.h" +#include + +namespace http +{ + LoginRequest::LoginRequest(ByteString username, ByteString password) : Request(ByteString::Build("https://", SERVER, "/Login.json")) + { + AddPostData(FormData{ + { "name", username }, + { "pass", password }, + }); + } + + LoginInfo LoginRequest::Finish() + { + auto [ status, data ] = Request::Finish(); + ParseResponse(data, status, responseJson); + LoginInfo loginInfo = { { 0, "" }, {} }; + try + { + Json::Value document; + std::istringstream ss(data); + ss >> document; + loginInfo.user.Username = document["Username"].asString(); + loginInfo.user.UserID = document["UserID"].asInt(); + loginInfo.user.SessionID = document["SessionID"].asString(); + loginInfo.user.SessionKey = document["SessionKey"].asString(); + loginInfo.user.UserElevation = User::ElevationFromString(document["Elevation"].asString()); + for (auto &item : document["Notifications"]) + { + loginInfo.notifications.push_back({ + ByteString(item["Text"].asString()).FromUtf8(), + item["Link"].asString(), + }); + } + } + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return loginInfo; + } +} diff --git a/src/client/http/LoginRequest.h b/src/client/http/LoginRequest.h new file mode 100644 index 000000000..2c430e7b5 --- /dev/null +++ b/src/client/http/LoginRequest.h @@ -0,0 +1,14 @@ +#pragma once +#include "Request.h" +#include "client/LoginInfo.h" + +namespace http +{ + class LoginRequest : public Request + { + public: + LoginRequest(ByteString username, ByteString password); + + LoginInfo Finish(); + }; +} diff --git a/src/client/http/LogoutRequest.cpp b/src/client/http/LogoutRequest.cpp new file mode 100644 index 000000000..40616834c --- /dev/null +++ b/src/client/http/LogoutRequest.cpp @@ -0,0 +1,16 @@ +#include "LogoutRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + LogoutRequest::LogoutRequest() : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Logout.json?Key=" + Client::Ref().GetAuthUser().SessionKey), authRequire, false) + { + } + + void LogoutRequest::Finish() + { + APIRequest::Finish(); + } +} diff --git a/src/client/http/LogoutRequest.h b/src/client/http/LogoutRequest.h new file mode 100644 index 000000000..46a725296 --- /dev/null +++ b/src/client/http/LogoutRequest.h @@ -0,0 +1,13 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class LogoutRequest : public APIRequest + { + public: + LogoutRequest(); + + void Finish(); + }; +} diff --git a/src/client/http/PostData.h b/src/client/http/PostData.h new file mode 100644 index 000000000..20bf75525 --- /dev/null +++ b/src/client/http/PostData.h @@ -0,0 +1,11 @@ +#pragma once +#include "common/String.h" +#include +#include + +namespace http +{ + using StringData = ByteString; + using FormData = std::map; + using PostData = std::variant; +}; diff --git a/src/client/http/PublishSaveRequest.cpp b/src/client/http/PublishSaveRequest.cpp new file mode 100644 index 000000000..fe934ede5 --- /dev/null +++ b/src/client/http/PublishSaveRequest.cpp @@ -0,0 +1,19 @@ +#include "PublishSaveRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + PublishSaveRequest::PublishSaveRequest(int saveID) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/View.json?ID=", saveID, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true) + { + AddPostData(FormData{ + { "ActionPublish", "bagels" }, + }); + } + + void PublishSaveRequest::Finish() + { + APIRequest::Finish(); + } +} diff --git a/src/client/http/PublishSaveRequest.h b/src/client/http/PublishSaveRequest.h new file mode 100644 index 000000000..112857fcd --- /dev/null +++ b/src/client/http/PublishSaveRequest.h @@ -0,0 +1,13 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class PublishSaveRequest : public APIRequest + { + public: + PublishSaveRequest(int saveID); + + void Finish(); + }; +} diff --git a/src/client/http/RemoveTagRequest.cpp b/src/client/http/RemoveTagRequest.cpp new file mode 100644 index 000000000..570941031 --- /dev/null +++ b/src/client/http/RemoveTagRequest.cpp @@ -0,0 +1,29 @@ +#include "RemoveTagRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + RemoveTagRequest::RemoveTagRequest(int saveID, ByteString tag) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/EditTag.json?Op=delete&ID=", saveID, "&Tag=", tag, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true) + { + } + + std::list RemoveTagRequest::Finish() + { + auto result = APIRequest::Finish(); + std::list tags; + try + { + for (auto &tag : result["Tags"]) + { + tags.push_back(tag.asString()); + } + } + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return tags; + } +} diff --git a/src/client/http/RemoveTagRequest.h b/src/client/http/RemoveTagRequest.h new file mode 100644 index 000000000..668f2f30d --- /dev/null +++ b/src/client/http/RemoveTagRequest.h @@ -0,0 +1,14 @@ +#pragma once +#include "APIRequest.h" +#include + +namespace http +{ + class RemoveTagRequest : public APIRequest + { + public: + RemoveTagRequest(int saveID, ByteString tag); + + std::list Finish(); + }; +} diff --git a/src/client/http/ReportSaveRequest.cpp b/src/client/http/ReportSaveRequest.cpp new file mode 100644 index 000000000..901f83118 --- /dev/null +++ b/src/client/http/ReportSaveRequest.cpp @@ -0,0 +1,19 @@ +#include "ReportSaveRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + ReportSaveRequest::ReportSaveRequest(int saveID, String message) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Report.json?ID=", saveID, "&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true) + { + AddPostData(FormData{ + { "Reason", message.ToUtf8() }, + }); + } + + void ReportSaveRequest::Finish() + { + APIRequest::Finish(); + } +} diff --git a/src/client/http/ReportSaveRequest.h b/src/client/http/ReportSaveRequest.h new file mode 100644 index 000000000..36b8e9519 --- /dev/null +++ b/src/client/http/ReportSaveRequest.h @@ -0,0 +1,13 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class ReportSaveRequest : public APIRequest + { + public: + ReportSaveRequest(int saveID, String message); + + void Finish(); + }; +} diff --git a/src/client/http/Request.cpp b/src/client/http/Request.cpp index 4ab9427ae..2478c6c23 100644 --- a/src/client/http/Request.cpp +++ b/src/client/http/Request.cpp @@ -1,139 +1,65 @@ #include "Request.h" - -#include "RequestManager.h" - -#ifndef NOHTTP -void SetupCurlEasyCiphers(CURL *easy) -{ -#ifdef SECURE_CIPHERS_ONLY - curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW); - ByteString ssl_type = version_info->ssl_version; - if (ssl_type.Contains("OpenSSL")) - { - curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, "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"); -#ifdef REQUEST_USE_CURL_TLSV13CL - curl_easy_setopt(easy, CURLOPT_TLS13_CIPHERS, "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"); -#endif - } - else if (ssl_type.Contains("Schannel")) - { - // TODO: add more cipher algorithms - curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, "CALG_ECDH_EPHEM"); - } -#endif - // TODO: Find out what TLS1.2 is supported on, might need to also allow TLS1.0 - curl_easy_setopt(easy, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2); -#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 70, 0) - curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT); -#elif defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 44, 0) - curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE); -#elif defined(WIN) -# error "That's unfortunate." -#endif -} -#endif +#include "requestmanager/RequestManager.h" +#include +#include +#include +#include namespace http { -#ifndef NOHTTP - Request::Request(ByteString uri_): - uri(uri_), - rm_total(0), - rm_done(0), - rm_finished(false), - rm_canceled(false), - rm_started(false), - added_to_multi(false), - status(0), - headers(NULL), -#ifdef REQUEST_USE_CURL_MIMEPOST - post_fields(NULL) -#else - post_fields_first(NULL), - post_fields_last(NULL) -#endif + Request::Request(ByteString newUri) { - easy = curl_easy_init(); - if (!RequestManager::Ref().AddRequest(this)) - { - status = 604; - rm_finished = true; - } + handle = RequestHandle::Create(); + handle->uri = newUri; } -#else - Request::Request(ByteString uri_) {} -#endif Request::~Request() { -#ifndef NOHTTP - curl_easy_cleanup(easy); -#ifdef REQUEST_USE_CURL_MIMEPOST - curl_mime_free(post_fields); -#else - curl_formfree(post_fields_first); -#endif - curl_slist_free_all(headers); -#endif + bool tryUnregister; + { + std::lock_guard lk(handle->stateMx); + tryUnregister = handle->state == RequestHandle::running; + } + if (tryUnregister) + { + // At this point it may have already finished and been unregistered but that's ok, + // attempting to unregister a request multiple times is allowed. We only do the + // state-checking dance so we don't wake up RequestManager if we don't have to. + // In fact, we could just not unregister requests here at all, they'd just run to + // completion and be unregistered later. All this does is cancel them early. + RequestManager::Ref().UnregisterRequest(*this); + } + } + + void Request::FailEarly(ByteString error) + { + assert(handle->state == RequestHandle::ready); + handle->failEarly = error; } void Request::Verb(ByteString newVerb) { -#ifndef NOHTTP - verb = newVerb; -#endif + assert(handle->state == RequestHandle::ready); + handle->verb = newVerb; } void Request::AddHeader(ByteString header) { -#ifndef NOHTTP - headers = curl_slist_append(headers, header.c_str()); -#endif + assert(handle->state == RequestHandle::ready); + handle->headers.push_back(header); } - // add post data to a request - void Request::AddPostData(std::map data) + void Request::AddPostData(PostData data) { -#ifndef NOHTTP + assert(handle->state == RequestHandle::ready); // Even if the map is empty, calling this function signifies you want to do a POST request - isPost = true; - if (!data.size()) - { - return; - } - - if (easy) - { -#ifdef REQUEST_USE_CURL_MIMEPOST - if (!post_fields) - { - post_fields = curl_mime_init(easy); - } - - for (auto &field : data) - { - curl_mimepart *part = curl_mime_addpart(post_fields); - curl_mime_data(part, &field.second[0], field.second.size()); - if (auto split = field.first.SplitBy(':')) - { - curl_mime_name(part, split.Before().c_str()); - curl_mime_filename(part, split.After().c_str()); - } - else - { - curl_mime_name(part, field.first.c_str()); - } - } -#else - post_fields_map.insert(data.begin(), data.end()); -#endif - } -#endif + handle->isPost = true; + handle->postData = data; } - // add userID and sessionID headers to the request void Request::AuthHeaders(ByteString ID, ByteString session) { + assert(handle->state == RequestHandle::ready); if (ID.size()) { if (session.size()) @@ -148,263 +74,81 @@ namespace http } } -#ifndef NOHTTP - size_t Request::HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata) - { - Request *req = (Request *)userdata; - auto actual_size = size * count; - if (actual_size >= 2 && ptr[actual_size - 2] == '\r' && ptr[actual_size - 1] == '\n') - { - if (actual_size > 2) // don't include header list terminator (but include the status line) - { - req->response_headers.push_back(ByteString(ptr, ptr + actual_size - 2)); - } - return actual_size; - } - return 0; - } - - size_t Request::WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata) - { - Request *req = (Request *)userdata; - auto actual_size = size * count; - req->response_body.append(ptr, actual_size); - return actual_size; - } -#endif - - // start the request thread void Request::Start() { -#ifndef NOHTTP - if (CheckStarted() || CheckDone()) - { - return; - } - - if (easy) - { -#ifdef REQUEST_USE_CURL_MIMEPOST - if (post_fields) - { - curl_easy_setopt(easy, CURLOPT_MIMEPOST, post_fields); - } -#else - if (!post_fields_map.empty()) - { - for (auto &field : post_fields_map) - { - if (auto split = field.first.SplitBy(':')) - { - curl_formadd(&post_fields_first, &post_fields_last, - CURLFORM_COPYNAME, split.Before().c_str(), - CURLFORM_BUFFER, split.After().c_str(), - CURLFORM_BUFFERPTR, &field.second[0], - CURLFORM_BUFFERLENGTH, field.second.size(), - CURLFORM_END); - } - else - { - curl_formadd(&post_fields_first, &post_fields_last, - CURLFORM_COPYNAME, field.first.c_str(), - CURLFORM_PTRCONTENTS, &field.second[0], - CURLFORM_CONTENTLEN, field.second.size(), - CURLFORM_END); - } - } - curl_easy_setopt(easy, CURLOPT_HTTPPOST, post_fields_first); - } -#endif - else if (isPost) - { - curl_easy_setopt(easy, CURLOPT_POST, 1L); - curl_easy_setopt(easy, CURLOPT_POSTFIELDS, ""); - } - else - { - curl_easy_setopt(easy, CURLOPT_HTTPGET, 1L); - } - if (verb.size()) - { - curl_easy_setopt(easy, CURLOPT_CUSTOMREQUEST, verb.c_str()); - } - - curl_easy_setopt(easy, CURLOPT_FOLLOWLOCATION, 1L); -#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0) -# ifdef ENFORCE_HTTPS - curl_easy_setopt(easy, CURLOPT_PROTOCOLS_STR, "https"); - curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS_STR, "https"); -# else - curl_easy_setopt(easy, CURLOPT_PROTOCOLS_STR, "https,http"); - curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS_STR, "https,http"); -# endif -#else -# ifdef ENFORCE_HTTPS - curl_easy_setopt(easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); - curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS); -# else - curl_easy_setopt(easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP); - curl_easy_setopt(easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP); -# endif -#endif - - SetupCurlEasyCiphers(easy); - - curl_easy_setopt(easy, CURLOPT_MAXREDIRS, 10L); - - curl_easy_setopt(easy, CURLOPT_ERRORBUFFER, error_buffer); - error_buffer[0] = 0; - - curl_easy_setopt(easy, CURLOPT_CONNECTTIMEOUT, timeout); - curl_easy_setopt(easy, CURLOPT_HTTPHEADER, headers); - curl_easy_setopt(easy, CURLOPT_URL, uri.c_str()); - - if (proxy.size()) - { - curl_easy_setopt(easy, CURLOPT_PROXY, proxy.c_str()); - } - if (cafile.size()) - { - curl_easy_setopt(easy, CURLOPT_CAINFO, cafile.c_str()); - } - if (capath.size()) - { - curl_easy_setopt(easy, CURLOPT_CAPATH, capath.c_str()); - } - - curl_easy_setopt(easy, CURLOPT_PRIVATE, (void *)this); - curl_easy_setopt(easy, CURLOPT_USERAGENT, user_agent.c_str()); - - curl_easy_setopt(easy, CURLOPT_HEADERDATA, (void *)this); - curl_easy_setopt(easy, CURLOPT_HEADERFUNCTION, Request::HeaderDataHandler); - - curl_easy_setopt(easy, CURLOPT_WRITEDATA, (void *)this); - curl_easy_setopt(easy, CURLOPT_WRITEFUNCTION, Request::WriteDataHandler); - } - - { - std::lock_guard g(rm_mutex); - rm_started = true; - } - RequestManager::Ref().StartRequest(this); -#endif + assert(handle->state == RequestHandle::ready); + handle->state = RequestHandle::running; + RequestManager::Ref().RegisterRequest(*this); } - - // finish the request (if called before the request is done, this will block) - ByteString Request::Finish(int *status_out, std::vector *headers_out) + bool Request::CheckDone() const + { + std::lock_guard lk(handle->stateMx); + assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done); + return handle->state == RequestHandle::done; + } + + std::pair Request::CheckProgress() const + { + std::lock_guard lk(handle->stateMx); + assert(handle->state == RequestHandle::running || handle->state == RequestHandle::done); + return { handle->bytesTotal, handle->bytesDone }; + } + + const std::vector &Request::ResponseHeaders() const + { + std::lock_guard lk(handle->stateMx); + assert(handle->state == RequestHandle::done); + return handle->responseHeaders; + } + + void Request::Wait() + { + std::unique_lock lk(handle->stateMx); + assert(handle->state == RequestHandle::running); + handle->stateCv.wait(lk, [this]() { + return handle->state == RequestHandle::done; + }); + } + + int Request::StatusCode() const { -#ifndef NOHTTP - if (CheckCanceled()) { - return ""; // shouldn't happen but just in case + std::unique_lock lk(handle->stateMx); + assert(handle->state == RequestHandle::done); } - - ByteString response_out; + return handle->statusCode; + } + + std::pair Request::Finish() + { { - std::unique_lock l(rm_mutex); - done_cv.wait(l, [this]() { return rm_finished; }); - rm_started = false; - rm_canceled = true; - if (status_out) - { - *status_out = status; - } - if (headers_out) - { - *headers_out = std::move(response_headers); - } - response_out = std::move(response_body); + std::unique_lock lk(handle->stateMx); + assert(handle->state == RequestHandle::done); } - - RequestManager::Ref().RemoveRequest(this); - return response_out; -#else - if (status_out) - *status_out = 604; - return ""; -#endif - } - - void Request::CheckProgress(int *total, int *done) - { -#ifndef NOHTTP - std::lock_guard g(rm_mutex); - if (total) + handle->state = RequestHandle::dead; + if (handle->error) { - *total = rm_total; + throw RequestError(*handle->error); } - if (done) + return std::pair{ handle->statusCode, std::move(handle->responseData) }; + } + + void RequestHandle::MarkDone() + { { - *done = rm_done; + std::lock_guard lk(stateMx); + assert(state == RequestHandle::running); + state = RequestHandle::done; } -#endif - } - - // returns true if the request has finished - bool Request::CheckDone() - { -#ifndef NOHTTP - std::lock_guard g(rm_mutex); - return rm_finished; -#else - return true; -#endif - } - - // returns true if the request was canceled - bool Request::CheckCanceled() - { -#ifndef NOHTTP - std::lock_guard g(rm_mutex); - return rm_canceled; -#else - return false; -#endif - } - - // returns true if the request is running - bool Request::CheckStarted() - { -#ifndef NOHTTP - std::lock_guard g(rm_mutex); - return rm_started; -#else - return true; -#endif - - } - - // cancels the request, the request thread will delete the Request* when it finishes (do not use Request in any way after canceling) - void Request::Cancel() - { -#ifndef NOHTTP + stateCv.notify_one(); + if (error) { - std::lock_guard g(rm_mutex); - rm_canceled = true; + std::cerr << *error << std::endl; } - RequestManager::Ref().RemoveRequest(this); -#endif } - ByteString Request::Simple(ByteString uri, int *status, std::map post_data) - { - return SimpleAuth(uri, status, "", "", post_data); - } - - ByteString Request::SimpleAuth(ByteString uri, int *status, ByteString ID, ByteString session, std::map post_data) - { - Request *request = new Request(uri); - if (!post_data.empty()) - { - request->AddPostData(post_data); - } - request->AuthHeaders(ID, session); - request->Start(); - return request->Finish(status); - } - - String StatusText(int ret) + const char *StatusText(int ret) { switch (ret) { @@ -475,7 +219,6 @@ namespace http case 607: return "Connection Refused"; case 608: return "Proxy Server Not Found"; case 609: return "SSL: Invalid Certificate Status"; - case 610: return "Cancelled by Shutdown"; case 611: return "Too Many Redirects"; case 612: return "SSL: Connect Error"; case 613: return "SSL: Crypto Engine Not Found"; @@ -487,8 +230,69 @@ namespace http case 619: return "SSL: Failed to Load CRL File"; case 620: return "SSL: Issuer Check Failed"; case 621: return "SSL: Pinned Public Key Mismatch"; - default: return "Unknown Status Code"; + } + return "Unknown Status Code"; + } + + void Request::ParseResponse(const ByteString &result, int status, ResponseType responseType) + { + // no server response, return "Malformed Response" + if (status == 200 && !result.size()) + { + status = 603; + } + if (status == 302) + { + return; + } + if (status != 200) + { + throw RequestError(ByteString::Build("HTTP Error ", status, ": ", http::StatusText(status))); + } + + switch (responseType) + { + case responseOk: + if (strncmp(result.c_str(), "OK", 2)) + { + throw RequestError(result); + } + break; + + case responseJson: + { + std::istringstream ss(result); + Json::Value root; + try + { + ss >> root; + // assume everything is fine if an empty [] is returned + if (root.size() == 0) + { + return; + } + int status = root.get("Status", 1).asInt(); + if (status != 1) + { + throw RequestError(ByteString(root.get("Error", "Unspecified Error").asString())); + } + } + catch (const std::exception &ex) + { + // sometimes the server returns a 200 with the text "Error: 401" + if (!strncmp(result.c_str(), "Error: ", 7)) + { + status = ByteString(result.begin() + 7, result.end()).ToNumber(); + throw RequestError(ByteString::Build("HTTP Error ", status, ": ", http::StatusText(status))); + } + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + } + break; + + case responseData: + // no further processing required + break; } } } - diff --git a/src/client/http/Request.h b/src/client/http/Request.h index 6a213feeb..2c6898499 100644 --- a/src/client/http/Request.h +++ b/src/client/http/Request.h @@ -1,91 +1,61 @@ -#ifndef REQUEST_H -#define REQUEST_H -#include "Config.h" - -#ifndef NOHTTP -# include -# include "common/tpt-minmax.h" // for MSVC, ensures windows.h doesn't cause compile errors by defining min/max -# include -# include -# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 55, 0) -# define REQUEST_USE_CURL_OFFSET_T -# endif -# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 56, 0) -# define REQUEST_USE_CURL_MIMEPOST -# endif -# if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 61, 0) -# define REQUEST_USE_CURL_TLSV13CL -# endif -#endif - -#include +#pragma once #include "common/String.h" +#include "PostData.h" +#include +#include +#include +#include +#include +#include namespace http { - class RequestManager; + struct RequestHandle; + + // Thrown by Finish and ParseResponse + struct RequestError : public std::runtime_error + { + using runtime_error::runtime_error; + }; + class Request { -#ifndef NOHTTP - ByteString uri; - std::vector response_headers; - ByteString response_body; - - CURL *easy; - char error_buffer[CURL_ERROR_SIZE]; - - volatile curl_off_t rm_total; - volatile curl_off_t rm_done; - volatile bool rm_finished; - volatile bool rm_canceled; - volatile bool rm_started; - std::mutex rm_mutex; - - bool added_to_multi; - int status; - - ByteString verb; - struct curl_slist *headers; - - bool isPost = false; -#ifdef REQUEST_USE_CURL_MIMEPOST - curl_mime *post_fields; -#else - curl_httppost *post_fields_first, *post_fields_last; - std::map post_fields_map; -#endif - - std::condition_variable done_cv; - - static size_t HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata); - static size_t WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata); -#endif + std::shared_ptr handle; public: - Request(ByteString uri); - virtual ~Request(); + Request(ByteString newUri); + Request(const Request &) = delete; + Request &operator =(const Request &) = delete; + ~Request(); + + void FailEarly(ByteString error); void Verb(ByteString newVerb); void AddHeader(ByteString header); - void AddPostData(std::map data); + + void AddPostData(PostData data); void AuthHeaders(ByteString ID, ByteString session); void Start(); - ByteString Finish(int *status, std::vector *headers = nullptr); - void Cancel(); + bool CheckDone() const; - void CheckProgress(int *total, int *done); - bool CheckDone(); - bool CheckCanceled(); - bool CheckStarted(); + std::pair CheckProgress() const; // total, done + const std::vector &ResponseHeaders() const; + void Wait(); + + int StatusCode() const; // status + std::pair Finish(); // status, data + + enum ResponseType + { + responseOk, + responseJson, + responseData, + }; + static void ParseResponse(const ByteString &result, int status, ResponseType responseType); friend class RequestManager; - - static ByteString Simple(ByteString uri, int *status, std::map post_data = std::map{}); - static ByteString SimpleAuth(ByteString uri, int *status, ByteString ID, ByteString session, std::map post_data = std::map{}); }; - String StatusText(int code); + const char *StatusText(int code); } - -#endif // REQUEST_H diff --git a/src/client/http/RequestManager.cpp b/src/client/http/RequestManager.cpp deleted file mode 100644 index d3505ac56..000000000 --- a/src/client/http/RequestManager.cpp +++ /dev/null @@ -1,272 +0,0 @@ -#ifndef NOHTTP -#include "Request.h" // includes curl.h, needs to come first to silence a warning on windows -#include "RequestManager.h" - -#include - -const int curl_multi_wait_timeout_ms = 100; -const long curl_max_host_connections = 1; -const long curl_max_concurrent_streams = 50; - -namespace http -{ - const long timeout = 15; - ByteString proxy; - ByteString cafile; - ByteString capath; - ByteString user_agent; - - void RequestManager::Shutdown() - { - if (rt_shutting_down) - return; - { - std::lock_guard g(rt_mutex); - rt_shutting_down = true; - } - rt_cv.notify_one(); - - if (initialized) - { - worker_thread.join(); - - curl_multi_cleanup(multi); - multi = NULL; - curl_global_cleanup(); - } - } - - void RequestManager::Initialise(ByteString newProxy, ByteString newCafile, ByteString newCapath) - { - curl_global_init(CURL_GLOBAL_DEFAULT); - multi = curl_multi_init(); - if (multi) - { - curl_multi_setopt(multi, CURLMOPT_MAX_HOST_CONNECTIONS, curl_max_host_connections); -#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 67, 0) - curl_multi_setopt(multi, CURLMOPT_MAX_CONCURRENT_STREAMS, curl_max_concurrent_streams); -#endif - } - - proxy = newProxy; - cafile = newCafile; - capath = newCapath; - - user_agent = - "PowderToy/" MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) " (" - IDENT_PLATFORM - "; " IDENT_BUILD - "; M" MTOS(MOD_ID) - "; " IDENT - ") TPTPP/" MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) "." MTOS(BUILD_NUM) IDENT_RELTYPE "." MTOS(SNAPSHOT_ID); - - worker_thread = std::thread([this]() { Worker(); }); - initialized = true; - } - - void RequestManager::Worker() - { - bool shutting_down = false; - while (!shutting_down) - { - { - std::unique_lock l(rt_mutex); - if (!requests_added_to_multi) - { - while (!rt_shutting_down && requests_to_add.empty() && !requests_to_start && !requests_to_remove) - { - rt_cv.wait(l); - } - } - shutting_down = rt_shutting_down; - requests_to_remove = false; - requests_to_start = false; - for (Request *request : requests_to_add) - { - request->status = 0; - requests.insert(request); - } - requests_to_add.clear(); - } - - if (multi && requests_added_to_multi) - { - int dontcare; - struct CURLMsg *msg; - - curl_multi_wait(multi, nullptr, 0, curl_multi_wait_timeout_ms, &dontcare); - curl_multi_perform(multi, &dontcare); - while ((msg = curl_multi_info_read(multi, &dontcare))) - { - if (msg->msg == CURLMSG_DONE) - { - Request *request; - curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &request); - - int finish_with = 600; - - switch (msg->data.result) - { - case CURLE_OK: - long code; - curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code); - finish_with = (int)code; - break; - - case CURLE_UNSUPPORTED_PROTOCOL: finish_with = 601; break; - case CURLE_COULDNT_RESOLVE_HOST: finish_with = 602; break; - case CURLE_OPERATION_TIMEDOUT: finish_with = 605; break; - case CURLE_URL_MALFORMAT: finish_with = 606; break; - case CURLE_COULDNT_CONNECT: finish_with = 607; break; - case CURLE_COULDNT_RESOLVE_PROXY: finish_with = 608; break; - case CURLE_TOO_MANY_REDIRECTS: finish_with = 611; break; - - case CURLE_SSL_CONNECT_ERROR: finish_with = 612; break; - case CURLE_SSL_ENGINE_NOTFOUND: finish_with = 613; break; - case CURLE_SSL_ENGINE_SETFAILED: finish_with = 614; break; - case CURLE_SSL_CERTPROBLEM: finish_with = 615; break; - case CURLE_SSL_CIPHER: finish_with = 616; break; - case CURLE_SSL_ENGINE_INITFAILED: finish_with = 617; break; - case CURLE_SSL_CACERT_BADFILE: finish_with = 618; break; - case CURLE_SSL_CRL_BADFILE: finish_with = 619; break; - case CURLE_SSL_ISSUER_ERROR: finish_with = 620; break; - case CURLE_SSL_PINNEDPUBKEYNOTMATCH: finish_with = 621; break; - case CURLE_SSL_INVALIDCERTSTATUS: finish_with = 609; break; - - case CURLE_HTTP2: - case CURLE_HTTP2_STREAM: - - case CURLE_FAILED_INIT: - case CURLE_NOT_BUILT_IN: - default: - break; - } - - if (finish_with >= 600) - { - std::cerr << request->error_buffer << std::endl; - } - - request->status = finish_with; - } - }; - } - - std::set requests_to_remove; - for (Request *request : requests) - { - bool signal_done = false; - - { - std::lock_guard g(request->rm_mutex); - if (shutting_down) - { - // In the weird case that a http::Request::Simple* call is - // waiting on this Request, we should fail the request - // instead of cancelling it ourselves. - request->status = 610; - } - if (!request->rm_canceled && request->rm_started && !request->added_to_multi && !request->status) - { - if (multi && request->easy) - { - MultiAdd(request); - } - else - { - request->status = 604; - } - } - if (!request->rm_canceled && request->rm_started && !request->rm_finished) - { - if (multi && request->easy) - { -#ifdef REQUEST_USE_CURL_OFFSET_T - curl_easy_getinfo(request->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &request->rm_total); - curl_easy_getinfo(request->easy, CURLINFO_SIZE_DOWNLOAD_T, &request->rm_done); -#else - double total, done; - curl_easy_getinfo(request->easy, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &total); - curl_easy_getinfo(request->easy, CURLINFO_SIZE_DOWNLOAD, &done); - request->rm_total = (curl_off_t)total; - request->rm_done = (curl_off_t)done; -#endif - } - if (request->status) - { - request->rm_finished = true; - MultiRemove(request); - signal_done = true; - } - } - if (request->rm_canceled) - { - requests_to_remove.insert(request); - } - } - - if (signal_done) - { - request->done_cv.notify_one(); - } - } - for (Request *request : requests_to_remove) - { - requests.erase(request); - MultiRemove(request); - delete request; - } - } - } - - void RequestManager::MultiAdd(Request *request) - { - if (multi && request->easy && !request->added_to_multi) - { - curl_multi_add_handle(multi, request->easy); - request->added_to_multi = true; - ++requests_added_to_multi; - } - } - - void RequestManager::MultiRemove(Request *request) - { - if (request->added_to_multi) - { - curl_multi_remove_handle(multi, request->easy); - request->added_to_multi = false; - --requests_added_to_multi; - } - } - - bool RequestManager::AddRequest(Request *request) - { - if (!initialized) - return false; - { - std::lock_guard g(rt_mutex); - requests_to_add.insert(request); - } - rt_cv.notify_one(); - return true; - } - - void RequestManager::StartRequest(Request *request) - { - { - std::lock_guard g(rt_mutex); - requests_to_start = true; - } - rt_cv.notify_one(); - } - - void RequestManager::RemoveRequest(Request *request) - { - { - std::lock_guard g(rt_mutex); - requests_to_remove = true; - } - rt_cv.notify_one(); - } -} -#endif diff --git a/src/client/http/RequestManager.h b/src/client/http/RequestManager.h deleted file mode 100644 index c3f3617a1..000000000 --- a/src/client/http/RequestManager.h +++ /dev/null @@ -1,61 +0,0 @@ -#include "Config.h" -#ifndef NOHTTP -#ifndef REQUESTMANAGER_H -#define REQUESTMANAGER_H - -#include "Config.h" -#include "common/tpt-minmax.h" // for MSVC, ensures windows.h doesn't cause compile errors by defining min/max -#include -#include -#include -#include -#include -#include "common/Singleton.h" -#include "common/String.h" - -namespace http -{ - class Request; - class RequestManager : public Singleton - { - std::thread worker_thread; - std::set requests; - int requests_added_to_multi = 0; - - std::set requests_to_add; - bool requests_to_start = false; - bool requests_to_remove = false; - bool initialized = false; - bool rt_shutting_down = false; - std::mutex rt_mutex; - std::condition_variable rt_cv; - - CURLM *multi = nullptr; - - void Start(); - void Worker(); - void MultiAdd(Request *request); - void MultiRemove(Request *request); - bool AddRequest(Request *request); - void StartRequest(Request *request); - void RemoveRequest(Request *request); - - public: - RequestManager() { } - ~RequestManager() { } - - void Initialise(ByteString newProxy, ByteString newCafile, ByteString newCapath); - void Shutdown(); - - friend class Request; - }; - - extern const long timeout; - extern ByteString proxy; - extern ByteString cafile; - extern ByteString capath; - extern ByteString user_agent; -} - -#endif // REQUESTMANAGER_H -#endif diff --git a/src/client/http/RequestMonitor.h b/src/client/http/RequestMonitor.h deleted file mode 100644 index 10ba69d30..000000000 --- a/src/client/http/RequestMonitor.h +++ /dev/null @@ -1,56 +0,0 @@ -#ifndef REQUESTMONITOR_H -#define REQUESTMONITOR_H - -#include -#include - -namespace http -{ - template - class RequestMonitor - { - R *request; - - protected: - RequestMonitor() : - request(nullptr) - { - } - - virtual ~RequestMonitor() - { - if (request) - { - request->Cancel(); - } - } - - void RequestPoll() - { - if (request && request->CheckDone()) - { - OnResponse(request->Finish()); - request = nullptr; - } - } - - template - void RequestSetup(Args&&... args) - { - assert(!request); - request = new R(std::forward(args)...); - } - - void RequestStart() - { - assert(request); - request->Start(); - } - - virtual void OnResponse(typename std::invoke_result::type v) = 0; - }; -} - -#endif // REQUESTMONITOR_H - - diff --git a/src/client/http/SaveUserInfoRequest.cpp b/src/client/http/SaveUserInfoRequest.cpp index cd579eb4d..4e0983c4a 100644 --- a/src/client/http/SaveUserInfoRequest.cpp +++ b/src/client/http/SaveUserInfoRequest.cpp @@ -1,37 +1,20 @@ #include "SaveUserInfoRequest.h" - #include "Config.h" -#include "client/UserInfo.h" namespace http { - SaveUserInfoRequest::SaveUserInfoRequest(UserInfo &info) : - APIRequest(SCHEME SERVER "/Profile.json") + SaveUserInfoRequest::SaveUserInfoRequest(UserInfo info) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Profile.json"), authRequire, true) { - AddPostData({ + AddPostData(FormData{ { "Location", info.location.ToUtf8() }, - { "Biography", info.biography.ToUtf8() } + { "Biography", info.biography.ToUtf8() }, }); } - SaveUserInfoRequest::~SaveUserInfoRequest() + void SaveUserInfoRequest::Finish() { - } - - bool SaveUserInfoRequest::Finish() - { - auto result = APIRequest::Finish(); - // Note that at this point it's not safe to use any member of the - // SaveUserInfoRequest object as Request::Finish signals RequestManager - // to delete it. - if (result.document) - { - return (*result.document)["Status"].asInt() == 1; - } - else - { - return false; - } + APIRequest::Finish(); } } diff --git a/src/client/http/SaveUserInfoRequest.h b/src/client/http/SaveUserInfoRequest.h index c9b63055f..006fafa24 100644 --- a/src/client/http/SaveUserInfoRequest.h +++ b/src/client/http/SaveUserInfoRequest.h @@ -1,21 +1,14 @@ -#ifndef SAVEUSERINFOREQUEST2_H -#define SAVEUSERINFOREQUEST2_H - +#pragma once #include "APIRequest.h" - -class UserInfo; +#include "client/UserInfo.h" namespace http { class SaveUserInfoRequest : public APIRequest { public: - SaveUserInfoRequest(UserInfo &info); - virtual ~SaveUserInfoRequest(); + SaveUserInfoRequest(UserInfo info); - bool Finish(); + void Finish(); }; } - -#endif // SAVEUSERINFOREQUEST2_H - diff --git a/src/client/http/SearchSavesRequest.cpp b/src/client/http/SearchSavesRequest.cpp new file mode 100644 index 000000000..bf358e149 --- /dev/null +++ b/src/client/http/SearchSavesRequest.cpp @@ -0,0 +1,85 @@ +#include "SearchSavesRequest.h" +#include "Config.h" +#include "client/Client.h" +#include "client/GameSave.h" +#include "Format.h" + +namespace http +{ + static ByteString Url(int start, int count, ByteString query, Sort sort, Category category) + { + ByteStringBuilder builder; + builder << SCHEME << SERVER << "/Browse.json?Start=" << start << "&Count=" << count; + auto appendToQuery = [&query](ByteString str) { + if (query.size()) + { + query += " "; + } + query += str; + }; + switch (sort) + { + case sortByDate: + appendToQuery("sort:date"); + break; + + default: + break; + } + auto user = Client::Ref().GetAuthUser(); + switch (category) + { + case categoryFavourites: + builder << "&Category=Favourites"; + break; + + case categoryMyOwn: + assert(user.UserID); + appendToQuery("user:" + user.Username); + break; + + default: + break; + } + if (query.size()) + { + builder << "&Search_Query=" << format::URLEncode(query); + } + return builder.Build(); + } + + SearchSavesRequest::SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category) : APIRequest(Url(start, count, query, sort, category), authUse, false) + { + } + + std::pair>> SearchSavesRequest::Finish() + { + std::vector> saves; + auto result = APIRequest::Finish(); + int count; + try + { + count = result["Count"].asInt(); + for (auto &save : result["Saves"]) + { + auto saveInfo = std::make_unique( + save["ID"].asInt(), + save["Created"].asInt64(), + save["Updated"].asInt64(), + save["ScoreUp"].asInt(), + save["ScoreDown"].asInt(), + save["Username"].asString(), + ByteString(save["Name"].asString()).FromUtf8() + ); + saveInfo->Version = save["Version"].asInt(); + saveInfo->SetPublished(save["Published"].asBool()); + saves.push_back(std::move(saveInfo)); + } + } + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return std::pair{ count, std::move(saves) }; + } +} diff --git a/src/client/http/SearchSavesRequest.h b/src/client/http/SearchSavesRequest.h new file mode 100644 index 000000000..5a275bc48 --- /dev/null +++ b/src/client/http/SearchSavesRequest.h @@ -0,0 +1,15 @@ +#pragma once +#include "APIRequest.h" +#include "client/SaveInfo.h" +#include "client/Search.h" + +namespace http +{ + class SearchSavesRequest : public APIRequest + { + public: + SearchSavesRequest(int start, int count, ByteString query, Sort sort, Category category); + + std::pair>> Finish(); + }; +} diff --git a/src/client/http/SearchTagsRequest.cpp b/src/client/http/SearchTagsRequest.cpp new file mode 100644 index 000000000..d4f39a700 --- /dev/null +++ b/src/client/http/SearchTagsRequest.cpp @@ -0,0 +1,42 @@ +#include "SearchTagsRequest.h" +#include "Config.h" +#include "Format.h" + +namespace http +{ + static ByteString Url(int start, int count, ByteString query) + { + ByteStringBuilder builder; + builder << SCHEME << SERVER << "/Browse/Tags.json?Start=" << start << "&Count=" << count; + if (query.size()) + { + builder << "&Search_Query=" << format::URLEncode(query); + } + return builder.Build(); + } + + SearchTagsRequest::SearchTagsRequest(int start, int count, ByteString query) : APIRequest(Url(start, count, query), authOmit, false) + { + } + + std::vector> SearchTagsRequest::Finish() + { + std::vector> tags; + auto result = APIRequest::Finish(); + try + { + for (auto &tag : result["Tags"]) + { + tags.push_back({ + tag["Tag"].asString(), + tag["Count"].asInt(), + }); + } + } + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return tags; + } +} diff --git a/src/client/http/SearchTagsRequest.h b/src/client/http/SearchTagsRequest.h new file mode 100644 index 000000000..4bb410bb6 --- /dev/null +++ b/src/client/http/SearchTagsRequest.h @@ -0,0 +1,13 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class SearchTagsRequest : public APIRequest + { + public: + SearchTagsRequest(int start, int count, ByteString query); + + std::vector> Finish(); + }; +} diff --git a/src/client/http/StartupRequest.cpp b/src/client/http/StartupRequest.cpp new file mode 100644 index 000000000..c3c036b88 --- /dev/null +++ b/src/client/http/StartupRequest.cpp @@ -0,0 +1,102 @@ +#include "StartupRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + // TODO: update Client::messageOfTheDay + StartupRequest::StartupRequest(bool newAlternate) : + Request(ByteString::Build(SCHEME, newAlternate ? UPDATESERVER : SERVER, "/Startup.json")), + alternate(newAlternate) + { + auto user = Client::Ref().GetAuthUser(); + if (user.UserID) + { + if (alternate) + { + // Cursed + AuthHeaders(user.Username, ""); + } + else + { + AuthHeaders(ByteString::Build(user.UserID), user.SessionID); + } + } + } + + StartupInfo StartupRequest::Finish() + { + auto [ status, data ] = Request::Finish(); + ParseResponse(data, status, responseJson); + StartupInfo startupInfo; + try + { + Json::Value document; + std::istringstream ss(data); + ss >> document; + startupInfo.sessionGood = document["Session"].asBool(); + startupInfo.messageOfTheDay = ByteString(document["MessageOfTheDay"].asString()).FromUtf8(); + for (auto ¬ification : document["Notifications"]) + { + startupInfo.notifications.push_back({ + ByteString(notification["Text"].asString()).FromUtf8(), + notification["Link"].asString() + }); + } + if constexpr (!IGNORE_UPDATES) + { + auto &versions = document["Updates"]; + auto parseUpdate = [this, &versions, &startupInfo](ByteString key, UpdateInfo::Channel channel, std::function updateAvailableFunc) { + if (!versions.isMember(key)) + { + return; + } + auto &info = versions[key]; + auto getOr = [&info](ByteString key, int defaultValue) -> int { + if (!info.isMember(key)) + { + return defaultValue; + } + return info[key].asInt(); + }; + auto build = getOr(key == "Snapshot" ? "Snapshot" : "Build", -1); + if (!updateAvailableFunc(build)) + { + return; + } + startupInfo.updateInfo = UpdateInfo{ + channel, + ByteString::Build(SCHEME, alternate ? UPDATESERVER : SERVER, info["File"].asString()), + ByteString(info["Changelog"].asString()).FromUtf8(), + getOr("Major", -1), + getOr("Minor", -1), + build, + }; + }; + if constexpr (SNAPSHOT || MOD) + { + parseUpdate("Snapshot", UpdateInfo::channelSnapshot, [](int build) -> bool { + return build > SNAPSHOT_ID; + }); + } + else + { + parseUpdate("Stable", UpdateInfo::channelStable, [](int build) -> bool { + return build > BUILD_NUM; + }); + if (!startupInfo.updateInfo.has_value()) + { + parseUpdate("Beta", UpdateInfo::channelBeta, [](int build) -> bool { + return build > BUILD_NUM; + }); + } + } + } + } + catch (const std::exception &ex) + { + throw RequestError("Could not read response: " + ByteString(ex.what())); + } + return startupInfo; + } +} diff --git a/src/client/http/StartupRequest.h b/src/client/http/StartupRequest.h new file mode 100644 index 000000000..2ca616b85 --- /dev/null +++ b/src/client/http/StartupRequest.h @@ -0,0 +1,16 @@ +#pragma once +#include "APIRequest.h" +#include "client/StartupInfo.h" + +namespace http +{ + class StartupRequest : public Request + { + bool alternate; + + public: + StartupRequest(bool newAlternate); + + StartupInfo Finish(); + }; +} diff --git a/src/client/http/ThumbnailRequest.cpp b/src/client/http/ThumbnailRequest.cpp index 0389b5464..9d7fef20e 100644 --- a/src/client/http/ThumbnailRequest.cpp +++ b/src/client/http/ThumbnailRequest.cpp @@ -1,19 +1,14 @@ #include "ThumbnailRequest.h" - #include "Config.h" namespace http { - ThumbnailRequest::ThumbnailRequest(int saveID, int saveDate, int width, int height) : + ThumbnailRequest::ThumbnailRequest(int saveID, int saveDate, Vec2 size) : ImageRequest(( saveDate - ? ByteString::Build(STATICSCHEME STATICSERVER "/", saveID, "_", saveDate, "_small.png") - : ByteString::Build(STATICSCHEME STATICSERVER "/", saveID, "_small.png") - ), width, height) - { - } - - ThumbnailRequest::~ThumbnailRequest() + ? ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_", saveDate, "_small.png") + : ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_small.png") + ), size) { } } diff --git a/src/client/http/ThumbnailRequest.h b/src/client/http/ThumbnailRequest.h index b6f951ed8..9ac714837 100644 --- a/src/client/http/ThumbnailRequest.h +++ b/src/client/http/ThumbnailRequest.h @@ -1,6 +1,4 @@ -#ifndef THUMBNAILREQUEST2_H -#define THUMBNAILREQUEST2_H - +#pragma once #include "ImageRequest.h" namespace http @@ -8,10 +6,6 @@ namespace http class ThumbnailRequest : public ImageRequest { public: - ThumbnailRequest(int saveID, int saveDate, int width, int height); - virtual ~ThumbnailRequest(); + ThumbnailRequest(int saveID, int saveDate, Vec2 size); }; } - -#endif // THUMBNAILREQUEST2_H - diff --git a/src/client/http/UnpublishSaveRequest.cpp b/src/client/http/UnpublishSaveRequest.cpp new file mode 100644 index 000000000..b3e47dae8 --- /dev/null +++ b/src/client/http/UnpublishSaveRequest.cpp @@ -0,0 +1,17 @@ +#include "UnpublishSaveRequest.h" +#include "client/Client.h" +#include "Config.h" + +namespace http +{ + UnpublishSaveRequest::UnpublishSaveRequest(int saveID) : + APIRequest(ByteString::Build(SCHEME, SERVER, "/Browse/Delete.json?ID=", saveID, "&Mode=Unpublish&Key=", Client::Ref().GetAuthUser().SessionKey), authRequire, true) + { + } + + void UnpublishSaveRequest::Finish() + { + APIRequest::Finish(); + } +} + diff --git a/src/client/http/UnpublishSaveRequest.h b/src/client/http/UnpublishSaveRequest.h new file mode 100644 index 000000000..afc333a67 --- /dev/null +++ b/src/client/http/UnpublishSaveRequest.h @@ -0,0 +1,13 @@ +#pragma once +#include "APIRequest.h" + +namespace http +{ + class UnpublishSaveRequest : public APIRequest + { + public: + UnpublishSaveRequest(int saveID); + + void Finish(); + }; +} diff --git a/src/client/http/UpdateRequest.cpp b/src/client/http/UpdateRequest.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/http/UpdateRequest.h b/src/client/http/UpdateRequest.h new file mode 100644 index 000000000..e69de29bb diff --git a/src/client/http/UploadSaveRequest.cpp b/src/client/http/UploadSaveRequest.cpp new file mode 100644 index 000000000..ccb961066 --- /dev/null +++ b/src/client/http/UploadSaveRequest.cpp @@ -0,0 +1,49 @@ +#include "UploadSaveRequest.h" +#include "client/SaveInfo.h" +#include "client/Client.h" +#include "client/GameSave.h" +#include "Config.h" + +namespace http +{ + UploadSaveRequest::UploadSaveRequest(const SaveInfo &saveInfo) : Request(ByteString::Build(SCHEME, SERVER, "/Save.api")) + { + auto [ fromNewerVersion, gameData ] = saveInfo.GetGameSave()->Serialise(); + if (!gameData.size()) + { + FailEarly("Cannot serialize game save"); + return; + } + else if (ALLOW_FAKE_NEWER_VERSION && fromNewerVersion && saveInfo.GetPublished()) + { + FailEarly("Cannot publish save, incompatible with latest release version"); + return; + } + auto user = Client::Ref().GetAuthUser(); + if (!user.UserID) + { + FailEarly("Not authenticated"); + return; + } + AuthHeaders(ByteString::Build(user.UserID), user.SessionID); + AddPostData(FormData{ + { "Name", saveInfo.GetName().ToUtf8() }, + { "Description", saveInfo.GetDescription().ToUtf8() }, + { "Data:save.bin", ByteString(gameData.begin(), gameData.end()) }, + { "Publish", saveInfo.GetPublished() ? "Public" : "Private" }, + { "Key", user.SessionKey }, + }); + } + + int UploadSaveRequest::Finish() + { + auto [ status, data ] = Request::Finish(); + ParseResponse(data, status, responseOk); + int saveID = ByteString(data.begin() + 3, data.end()).ToNumber(); + if (!saveID) + { + throw RequestError("Server did not return Save ID"); + } + return saveID; + } +} diff --git a/src/client/http/UploadSaveRequest.h b/src/client/http/UploadSaveRequest.h new file mode 100644 index 000000000..29d07ed18 --- /dev/null +++ b/src/client/http/UploadSaveRequest.h @@ -0,0 +1,15 @@ +#pragma once +#include "Request.h" + +class SaveInfo; + +namespace http +{ + class UploadSaveRequest : public Request + { + public: + UploadSaveRequest(const SaveInfo &saveInfo); + + int Finish(); + }; +} diff --git a/src/client/http/meson.build b/src/client/http/meson.build index 836fcc62e..1048712b3 100644 --- a/src/client/http/meson.build +++ b/src/client/http/meson.build @@ -5,10 +5,25 @@ client_files += files( 'Request.cpp', 'SaveUserInfoRequest.cpp', 'ThumbnailRequest.cpp', + 'AddTagRequest.cpp', + 'RemoveTagRequest.cpp', + 'GetSaveRequest.cpp', + 'PublishSaveRequest.cpp', + 'UnpublishSaveRequest.cpp', + 'ReportSaveRequest.cpp', + 'FavouriteSaveRequest.cpp', + 'AddCommentRequest.cpp', + 'DeleteSaveRequest.cpp', + 'LoginRequest.cpp', + 'GetSaveDataRequest.cpp', + 'ExecVoteRequest.cpp', + 'UploadSaveRequest.cpp', + 'StartupRequest.cpp', + 'UpdateRequest.cpp', + 'SearchSavesRequest.cpp', + 'SearchTagsRequest.cpp', + 'GetCommentsRequest.cpp', + 'LogoutRequest.cpp', ) -if enable_http - client_files += files( - 'RequestManager.cpp', - ) -endif +subdir('requestmanager') diff --git a/src/client/http/requestmanager/Common.cpp b/src/client/http/requestmanager/Common.cpp new file mode 100644 index 000000000..a8d18c5c9 --- /dev/null +++ b/src/client/http/requestmanager/Common.cpp @@ -0,0 +1,46 @@ +#include "RequestManager.h" +#include "client/http/Request.h" +#include "Config.h" + +namespace http +{ + RequestManager::RequestManager(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork) : + proxy(newProxy), + cafile(newCafile), + capath(newCapath), + disableNetwork(newDisableNetwork) + { + userAgent = ByteString::Build( + "PowderToy/", SAVE_VERSION, ".", MINOR_VERSION, + " (", IDENT_PLATFORM, + "; NO", // Unused, used to be SSE level. + "; M", MOD_ID, + "; ", IDENT, + ") TPTPP/", SAVE_VERSION, ".", MINOR_VERSION, ".", BUILD_NUM, IDENT_RELTYPE, ".", SNAPSHOT_ID + ); + } + + void RequestManager::RegisterRequest(Request &request) + { + if (request.handle->failEarly) + { + request.handle->error = request.handle->failEarly.value(); + request.handle->statusCode = 600; + request.handle->MarkDone(); + return; + } + if (disableNetwork) + { + request.handle->statusCode = 604; + request.handle->error = "network disabled upon request"; + request.handle->MarkDone(); + return; + } + RegisterRequestImpl(request); + } + + void RequestManager::UnregisterRequest(Request &request) + { + UnregisterRequestImpl(request); + } +} diff --git a/src/client/http/requestmanager/CurlError.h b/src/client/http/requestmanager/CurlError.h new file mode 100644 index 000000000..959b54e8d --- /dev/null +++ b/src/client/http/requestmanager/CurlError.h @@ -0,0 +1,15 @@ +#pragma once +#include // Has to come first because windows(tm). +#include + +namespace http +{ + struct CurlError : public std::runtime_error + { + using runtime_error::runtime_error; + }; + + void SetupCurlEasyCiphers(CURL *easy); + void HandleCURLcode(CURLcode code); + void HandleCURLMcode(CURLMcode code); +} diff --git a/src/client/http/requestmanager/Libcurl.cpp b/src/client/http/requestmanager/Libcurl.cpp new file mode 100644 index 000000000..4e16fbdb3 --- /dev/null +++ b/src/client/http/requestmanager/Libcurl.cpp @@ -0,0 +1,546 @@ +#include // Has to come first because windows(tm). +#include "RequestManager.h" +#include "client/http/Request.h" +#include "CurlError.h" +#include "Config.h" + +#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 55, 0) +# define REQUEST_USE_CURL_OFFSET_T +#endif +#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 56, 0) +# define REQUEST_USE_CURL_MIMEPOST +#endif +#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 61, 0) +# define REQUEST_USE_CURL_TLSV13CL +#endif + +const long curlMaxHostConnections = 1; +const long curlMaxConcurrentStreams = 50; +const long curlConnectTimeoutS = 15; + +namespace http +{ + void HandleCURLcode(CURLcode code) + { + if (code != CURLE_OK) + { + throw CurlError(curl_easy_strerror(code)); + } + }; + + void HandleCURLMcode(CURLMcode code) + { + if (code != CURLM_OK && code != CURLM_CALL_MULTI_PERFORM) + { + throw CurlError(curl_multi_strerror(code)); + } + }; + +#ifndef REQUEST_USE_CURL_MIMEPOST + void HandleCURLFORMcode(CURLFORMcode code) + { + if (code != CURL_FORMADD_OK) + { + throw CurlError(ByteString::Build("CURLFORMcode ", code)); + } + }; +#endif + + struct RequestHandleHttp : public RequestHandle + { + curl_slist *curlHeaders = NULL; +#ifdef REQUEST_USE_CURL_MIMEPOST + curl_mime *curlPostFields = NULL; +#else + curl_httppost *curlPostFieldsFirst = NULL; + curl_httppost *curlPostFieldsLast = NULL; +#endif + CURL *curlEasy = NULL; + char curlErrorBuffer[CURL_ERROR_SIZE]; + bool curlAddedToMulti = false; + + RequestHandleHttp() : RequestHandle(CtorTag{}) + { + } + + static size_t HeaderDataHandler(char *ptr, size_t size, size_t count, void *userdata) + { + auto *handle = (RequestHandleHttp *)userdata; + auto bytes = size * count; + if (bytes >= 2 && ptr[bytes - 2] == '\r' && ptr[bytes - 1] == '\n') + { + if (bytes > 2) // Don't include header list terminator (but include the status line). + { + handle->responseHeaders.push_back(ByteString(ptr, ptr + bytes - 2)); + } + return bytes; + } + return 0; + } + + static size_t WriteDataHandler(char *ptr, size_t size, size_t count, void *userdata) + { + auto *handle = (RequestHandleHttp *)userdata; + auto bytes = size * count; + handle->responseData.append(ptr, bytes); + return bytes; + } + }; + + std::shared_ptr RequestHandle::Create() + { + return std::make_shared(); + } + + struct RequestManagerImpl : public RequestManager + { + using RequestManager::RequestManager; + + RequestManagerImpl(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork); + ~RequestManagerImpl(); + + std::thread worker; + void Worker(); + void WorkerInit(); + void WorkerPerform(); + void WorkerExit(); + + // State shared between Request threads and the worker thread. + std::vector> requestHandlesToRegister; + std::vector> requestHandlesToUnregister; + bool running = true; + std::mutex sharedStateMx; + + std::vector> requestHandles; + void RegisterRequestHandle(std::shared_ptr requestHandle); + void UnregisterRequestHandle(std::shared_ptr requestHandle); + + bool curlGlobalInit = false; + CURLM *curlMulti = NULL; + }; + + RequestManagerImpl::RequestManagerImpl(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork) : + RequestManager(newProxy, newCafile, newCapath, newDisableNetwork) + { + worker = std::thread([this]() { + Worker(); + }); + } + + RequestManagerImpl::~RequestManagerImpl() + { + { + std::lock_guard lk(sharedStateMx); + running = false; + } + worker.join(); + } + + void RequestManagerImpl::WorkerInit() + { + if (!curl_global_init(CURL_GLOBAL_DEFAULT)) + { + curlGlobalInit = true; + curlMulti = curl_multi_init(); + if (curlMulti) + { + HandleCURLMcode(curl_multi_setopt(curlMulti, CURLMOPT_MAX_HOST_CONNECTIONS, curlMaxHostConnections)); +#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 67, 0) + HandleCURLMcode(curl_multi_setopt(curlMulti, CURLMOPT_MAX_CONCURRENT_STREAMS, curlMaxConcurrentStreams)); +#endif + } + } + } + + void RequestManagerImpl::WorkerPerform() + { + auto manager = static_cast(this); + int dontcare; + HandleCURLMcode(curl_multi_poll(manager->curlMulti, NULL, 0, 1000, &dontcare)); + HandleCURLMcode(curl_multi_perform(manager->curlMulti, &dontcare)); + while (auto msg = curl_multi_info_read(manager->curlMulti, &dontcare)) + { + if (msg->msg == CURLMSG_DONE) + { + RequestHandleHttp *handle; + HandleCURLcode(curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &handle)); + handle->statusCode = 600; + switch (msg->data.result) + { + case CURLE_OK: + { + long code; + HandleCURLcode(curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &code)); + assert(code); + handle->statusCode = int(code); + } + break; + + case CURLE_UNSUPPORTED_PROTOCOL: handle->statusCode = 601; break; + case CURLE_COULDNT_RESOLVE_HOST: handle->statusCode = 602; break; + case CURLE_OPERATION_TIMEDOUT: handle->statusCode = 605; break; + case CURLE_URL_MALFORMAT: handle->statusCode = 606; break; + case CURLE_COULDNT_CONNECT: handle->statusCode = 607; break; + case CURLE_COULDNT_RESOLVE_PROXY: handle->statusCode = 608; break; + case CURLE_TOO_MANY_REDIRECTS: handle->statusCode = 611; break; + case CURLE_SSL_CONNECT_ERROR: handle->statusCode = 612; break; + case CURLE_SSL_ENGINE_NOTFOUND: handle->statusCode = 613; break; + case CURLE_SSL_ENGINE_SETFAILED: handle->statusCode = 614; break; + case CURLE_SSL_CERTPROBLEM: handle->statusCode = 615; break; + case CURLE_SSL_CIPHER: handle->statusCode = 616; break; + case CURLE_SSL_ENGINE_INITFAILED: handle->statusCode = 617; break; + case CURLE_SSL_CACERT_BADFILE: handle->statusCode = 618; break; + case CURLE_SSL_CRL_BADFILE: handle->statusCode = 619; break; + case CURLE_SSL_ISSUER_ERROR: handle->statusCode = 620; break; + case CURLE_SSL_PINNEDPUBKEYNOTMATCH: handle->statusCode = 621; break; + case CURLE_SSL_INVALIDCERTSTATUS: handle->statusCode = 609; break; + case CURLE_HTTP2: + case CURLE_HTTP2_STREAM: + case CURLE_FAILED_INIT: + case CURLE_NOT_BUILT_IN: + default: + break; + } + if (handle->statusCode >= 600) + { + handle->error = handle->curlErrorBuffer; + } + } + } + for (auto &requestHandle : requestHandles) + { + auto handle = static_cast(requestHandle.get()); + if (handle->curlEasy) + { +#ifdef REQUEST_USE_CURL_OFFSET_T + curl_off_t total, done; + HandleCURLcode(curl_easy_getinfo(handle->curlEasy, CURLINFO_CONTENT_LENGTH_DOWNLOAD_T, &total)); // stores -1 if unknown + HandleCURLcode(curl_easy_getinfo(handle->curlEasy, CURLINFO_SIZE_DOWNLOAD_T, &done)); +#else + double total, done; + HandleCURLcode(curl_easy_getinfo(handle->curlEasy, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &total)); // stores -1 if unknown + HandleCURLcode(curl_easy_getinfo(handle->curlEasy, CURLINFO_SIZE_DOWNLOAD, &done)); +#endif + handle->bytesTotal = int(total); + handle->bytesDone = int(done); + } + else + { + handle->bytesTotal = -1; + handle->bytesDone = 0; + } + } + } + + void RequestManagerImpl::WorkerExit() + { + curl_multi_cleanup(curlMulti); + curlMulti = NULL; + curl_global_cleanup(); + } + + void RequestManagerImpl::Worker() + { + WorkerInit(); + while (true) + { + { + std::lock_guard lk(sharedStateMx); + for (auto &requestHandle : requestHandles) + { + if (requestHandle->statusCode) + { + requestHandlesToUnregister.push_back(requestHandle); + } + } + for (auto &requestHandle : requestHandlesToRegister) + { + requestHandles.push_back(requestHandle); + RegisterRequestHandle(requestHandle); + } + requestHandlesToRegister.clear(); + for (auto &requestHandle : requestHandlesToUnregister) + { + auto eraseFrom = std::remove(requestHandles.begin(), requestHandles.end(), requestHandle); + if (eraseFrom != requestHandles.end()) + { + assert(eraseFrom + 1 == requestHandles.end()); + UnregisterRequestHandle(requestHandle); + requestHandles.erase(eraseFrom, requestHandles.end()); + requestHandle->MarkDone(); + } + } + requestHandlesToUnregister.clear(); + if (!running) + { + break; + } + } + WorkerPerform(); + } + assert(!requestHandles.size()); + WorkerExit(); + } + + void RequestManager::RegisterRequestImpl(Request &request) + { + auto manager = static_cast(this); + { + std::lock_guard lk(manager->sharedStateMx); + manager->requestHandlesToRegister.push_back(request.handle); + } + curl_multi_wakeup(manager->curlMulti); + } + + void RequestManager::UnregisterRequestImpl(Request &request) + { + auto manager = static_cast(this); + { + std::lock_guard lk(manager->sharedStateMx); + manager->requestHandlesToUnregister.push_back(request.handle); + } + curl_multi_wakeup(manager->curlMulti); + } + + void RequestManagerImpl::RegisterRequestHandle(std::shared_ptr requestHandle) + { + auto manager = static_cast(this); + auto handle = static_cast(requestHandle.get()); + auto failEarly = [&requestHandle](int statusCode, ByteString error) { + requestHandle->statusCode = statusCode; + requestHandle->error = error; + }; + if (!manager->curlGlobalInit) + { + return failEarly(600, "no CURL"); + } + if (!manager->curlMulti) + { + return failEarly(600, "no CURL multi handle"); + } + try + { + handle->curlEasy = curl_easy_init(); + if (!handle->curlEasy) + { + return failEarly(600, "no CURL easy handle"); + } + for (auto &header : handle->headers) + { + auto *newHeaders = curl_slist_append(handle->curlHeaders, header.c_str()); + if (!newHeaders) + { + // Hopefully this is what a NULL from curl_slist_append means. + HandleCURLcode(CURLE_OUT_OF_MEMORY); + } + handle->curlHeaders = newHeaders; + } + { + auto &postData = handle->postData; + if (std::holds_alternative(postData) && std::get(postData).size()) + { + auto &formData = std::get(postData); +#ifdef REQUEST_USE_CURL_MIMEPOST + handle->curlPostFields = curl_mime_init(handle->curlEasy); + if (!handle->curlPostFields) + { + // Hopefully this is what a NULL from curl_mime_init means. + HandleCURLcode(CURLE_OUT_OF_MEMORY); + } + for (auto &field : formData) + { + curl_mimepart *part = curl_mime_addpart(handle->curlPostFields); + if (!part) + { + // Hopefully this is what a NULL from curl_mime_addpart means. + HandleCURLcode(CURLE_OUT_OF_MEMORY); + } + HandleCURLcode(curl_mime_data(part, &field.second[0], field.second.size())); + if (auto split = field.first.SplitBy(':')) + { + HandleCURLcode(curl_mime_name(part, split.Before().c_str())); + HandleCURLcode(curl_mime_filename(part, split.After().c_str())); + } + else + { + HandleCURLcode(curl_mime_name(part, field.first.c_str())); + } + } + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_MIMEPOST, handle->curlPostFields)); +#else + for (auto &field : formData) + { + if (auto split = field.first.SplitBy(':')) + { + HandleCURLFORMcode(curl_formadd(&handle->curlPostFieldsFirst, &handle->curlPostFieldsLast, + CURLFORM_COPYNAME, split.Before().c_str(), + CURLFORM_BUFFER, split.After().c_str(), + CURLFORM_BUFFERPTR, &field.second[0], + CURLFORM_BUFFERLENGTH, field.second.size(), + CURLFORM_END)); + } + else + { + HandleCURLFORMcode(curl_formadd(&handle->curlPostFieldsFirst, &handle->curlPostFieldsLast, + CURLFORM_COPYNAME, field.first.c_str(), + CURLFORM_PTRCONTENTS, &field.second[0], + CURLFORM_CONTENTLEN, field.second.size(), + CURLFORM_END)); + } + } + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPPOST, handle->curlPostFieldsFirst)); +#endif + } + else if (std::holds_alternative(postData) && std::get(postData).size()) + { + auto &stringData = std::get(postData); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POSTFIELDS, &stringData[0])); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POSTFIELDSIZE_LARGE, curl_off_t(stringData.size()))); + } + else if (handle->isPost) + { + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POST, 1L)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_POSTFIELDS, "")); + } + else + { + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPGET, 1L)); + } + if (handle->verb.size()) + { + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CUSTOMREQUEST, handle->verb.c_str())); + } + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_FOLLOWLOCATION, 1L)); + if constexpr (ENFORCE_HTTPS) + { +#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0) + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS_STR, "https")); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS_STR, "https")); +#else + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS)); +#endif + } + else + { +#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0) + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS_STR, "https,http")); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS_STR, "https,http")); +#else + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS | CURLPROTO_HTTP)); +#endif + } + SetupCurlEasyCiphers(handle->curlEasy); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_MAXREDIRS, 10L)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_ERRORBUFFER, handle->curlErrorBuffer)); + handle->curlErrorBuffer[0] = 0; + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CONNECTTIMEOUT, curlConnectTimeoutS)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HTTPHEADER, handle->curlHeaders)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_URL, handle->uri.c_str())); + if (proxy.size()) + { + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PROXY, proxy.c_str())); + } + if (cafile.size()) + { + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CAINFO, cafile.c_str())); + } + if (capath.size()) + { + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_CAPATH, capath.c_str())); + } + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_PRIVATE, (void *)handle)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_USERAGENT, userAgent.c_str())); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HEADERDATA, (void *)handle)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_HEADERFUNCTION, &RequestHandleHttp::HeaderDataHandler)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_WRITEDATA, (void *)handle)); + HandleCURLcode(curl_easy_setopt(handle->curlEasy, CURLOPT_WRITEFUNCTION, &RequestHandleHttp::WriteDataHandler)); + } + } + catch (const CurlError &ex) + { + return failEarly(600, ex.what()); + } + HandleCURLMcode(curl_multi_add_handle(manager->curlMulti, handle->curlEasy)); + handle->curlAddedToMulti = true; + } + + void RequestManagerImpl::UnregisterRequestHandle(std::shared_ptr requestHandle) + { + auto manager = static_cast(this); + auto handle = static_cast(requestHandle.get()); + if (handle->curlAddedToMulti) + { + HandleCURLMcode(curl_multi_remove_handle(manager->curlMulti, handle->curlEasy)); + handle->curlAddedToMulti = false; + } + curl_easy_cleanup(handle->curlEasy); +#ifdef REQUEST_USE_CURL_MIMEPOST + curl_mime_free(handle->curlPostFields); +#else + curl_formfree(handle->curlPostFieldsFirst); +#endif + curl_slist_free_all(handle->curlHeaders); + } + + RequestManagerPtr RequestManager::Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork) + { + return RequestManagerPtr(new RequestManagerImpl(newProxy, newCafile, newCapath, newDisableNetwork)); + } + + void RequestManagerDeleter::operator ()(RequestManager *ptr) const + { + delete static_cast(ptr); + } + + void SetupCurlEasyCiphers(CURL *easy) + { + if constexpr (SECURE_CIPHERS_ONLY) + { + curl_version_info_data *version_info = curl_version_info(CURLVERSION_NOW); + ByteString ssl_type = version_info->ssl_version; + if (ssl_type.Contains("OpenSSL") || ssl_type.Contains("mbedTLS")) + { + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, + "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" + )); +#ifdef REQUEST_USE_CURL_TLSV13CL + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_TLS13_CIPHERS, + "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" + )); +#endif + } + else if (ssl_type.Contains("Schannel")) + { + // TODO: add more cipher algorithms + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_CIPHER_LIST, "CALG_ECDH_EPHEM")); + } + } + // TODO: Find out what TLS1.2 is supported on, might need to also allow TLS1.0 + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2)); +#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 70, 0) + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_REVOKE_BEST_EFFORT)); +#elif defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 44, 0) + HandleCURLcode(curl_easy_setopt(easy, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NO_REVOKE)); +#endif + } +} diff --git a/src/client/http/requestmanager/Null.cpp b/src/client/http/requestmanager/Null.cpp new file mode 100644 index 000000000..6e1e4ece3 --- /dev/null +++ b/src/client/http/requestmanager/Null.cpp @@ -0,0 +1,31 @@ +#include "RequestManager.h" +#include "client/http/Request.h" + +namespace http +{ + std::shared_ptr RequestHandle::Create() + { + return std::make_shared(CtorTag{}); + } + + void RequestManager::RegisterRequestImpl(Request &request) + { + request.handle->statusCode = 604; + request.handle->error = "network support not compiled in"; + request.handle->MarkDone(); + } + + void RequestManager::UnregisterRequestImpl(Request &request) + { + } + + RequestManagerPtr RequestManager::Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork) + { + return RequestManagerPtr(new RequestManager(newProxy, newCafile, newCapath, newDisableNetwork)); + } + + void RequestManagerDeleter::operator ()(RequestManager *ptr) const + { + delete ptr; + } +} diff --git a/src/client/http/requestmanager/RequestManager.h b/src/client/http/requestmanager/RequestManager.h new file mode 100644 index 000000000..a5e82ae56 --- /dev/null +++ b/src/client/http/requestmanager/RequestManager.h @@ -0,0 +1,92 @@ +#pragma once +#include "common/ExplicitSingleton.h" +#include "common/String.h" +#include "client/http/PostData.h" +#include +#include +#include +#include +#include +#include +#include + +namespace http +{ + class Request; + + struct RequestHandle + { + protected: + struct CtorTag + { + }; + + public: + ByteString uri; + ByteString verb; + bool isPost = false; + PostData postData; + std::vector headers; + + enum State + { + ready, + running, + done, + dead, + }; + State state = ready; + std::mutex stateMx; + std::condition_variable stateCv; + std::atomic bytesTotal = -1; + std::atomic bytesDone = 0; + int statusCode = 0; + ByteString responseData; + std::vector responseHeaders; + std::optional error; + std::optional failEarly; + + RequestHandle(CtorTag) + { + } + + RequestHandle(const RequestHandle &) = delete; + RequestHandle &operator =(const RequestHandle &) = delete; + + void MarkDone(); + + static std::shared_ptr Create(); + }; + + class RequestManager; + struct RequestManagerDeleter + { + void operator ()(RequestManager *ptr) const; + }; + using RequestManagerPtr = std::unique_ptr; + class RequestManager : public ExplicitSingleton + { + protected: + ByteString proxy; + ByteString cafile; + ByteString capath; + ByteString userAgent; + bool disableNetwork; + + RequestManager(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork); + + void RegisterRequestImpl(Request &request); + void UnregisterRequestImpl(Request &request); + + public: + void RegisterRequest(Request &request); + void UnregisterRequest(Request &request); + + bool DisableNetwork() const + { + return disableNetwork; + } + + static RequestManagerPtr Create(ByteString newProxy, ByteString newCafile, ByteString newCapath, bool newDisableNetwork); + }; +} diff --git a/src/client/http/requestmanager/meson.build b/src/client/http/requestmanager/meson.build new file mode 100644 index 000000000..8ee8603e7 --- /dev/null +++ b/src/client/http/requestmanager/meson.build @@ -0,0 +1,10 @@ +client_files += files( + 'Common.cpp', +) + +if enable_http + client_files += files('Libcurl.cpp') +else + client_files += files('Null.cpp') +endif +conf_data.set('NOHTTP', not enable_http ? 'true' : 'false') diff --git a/src/client/meson.build b/src/client/meson.build index e6711da8e..55503feee 100644 --- a/src/client/meson.build +++ b/src/client/meson.build @@ -5,6 +5,7 @@ client_files = files( 'ThumbnailRendererTask.cpp', 'Client.cpp', 'GameSave.cpp', + 'User.cpp', ) subdir('http') diff --git a/src/common/ExplicitSingleton.h b/src/common/ExplicitSingleton.h new file mode 100644 index 000000000..a21887b76 --- /dev/null +++ b/src/common/ExplicitSingleton.h @@ -0,0 +1,31 @@ +#pragma once +#include + +template +class ExplicitSingleton +{ + static Type *&Instance() + { + // [dcl.fct.spec]: A static local variable in an extern inline function always refers to the same object. + static Type *instance = nullptr; + return instance; + } + +public: + ExplicitSingleton() + { + auto &instance = Instance(); + assert(!instance); + instance = static_cast(this); + } + + ~ExplicitSingleton() + { + Instance() = nullptr; + } + + static Type &Ref() + { + return *Instance(); + } +}; diff --git a/src/common/Plane.h b/src/common/Plane.h new file mode 100644 index 000000000..ab75344eb --- /dev/null +++ b/src/common/Plane.h @@ -0,0 +1,170 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "common/Vec2.h" + +constexpr size_t DynamicExtent = std::numeric_limits::max(); + +template +struct extentStorage +{ + constexpr extentStorage(size_t) + {} + + constexpr size_t getExtent() const + { + return Extent; + } + + constexpr void setExtent(size_t) + {} +}; + +template<> +struct extentStorage +{ + size_t extent; + + constexpr extentStorage(size_t extent): + extent(extent) + {} + + constexpr size_t getExtent() const + { + return extent; + } + + constexpr void setExtent(size_t extent) + { + this->extent = extent; + } +}; + +template +struct xExtent: extentStorage +{ + using extentStorage::extentStorage; +}; + +template +struct yExtent: extentStorage +{ + using extentStorage::extentStorage; +}; + +template +struct baseStorage +{ + using type = T; +}; + +template +struct baseStorage +{ + using type = std::reference_wrapper; +}; + +template +struct baseStorage +{ + using type = std::reference_wrapper; +}; + +// A class that contains some container T and lets you index into it as if it +// were a 2D array of size Width x Height, in row-major order. +template +class PlaneAdapter: xExtent, yExtent +{ + using value_type = std::remove_reference_t()[0])>; + using iterator = decltype(std::begin(std::declval())); + using const_iterator = decltype(std::begin(std::declval())); + + size_t getWidth() const + { + return xExtent::getExtent(); + } + + size_t getHeight() const + { + return yExtent::getExtent(); + } + + std::remove_reference_t &getBase() + { + return Base; + } + + std::remove_reference_t const &getBase() const + { + return Base; + } + +public: + typename baseStorage::type Base; + + PlaneAdapter(): + xExtent(0), + yExtent(0), + Base() + {} + + template + PlaneAdapter(Vec2 size, Args&&... args): + xExtent(size.X), + yExtent(size.Y), + Base(getWidth() * getHeight(), std::forward(args)...) + {} + + template + PlaneAdapter(Vec2 size, std::in_place_t, Args&&... args): + xExtent(size.X), + yExtent(size.Y), + Base(std::forward(args)...) + {} + + Vec2 Size() const + { + return Vec2(getWidth(), getHeight()); + } + + void SetSize(Vec2 size) + { + xExtent::setExtent(size.X); + yExtent::setExtent(size.Y); + } + + iterator RowIterator(Vec2 p) + { + return std::begin(getBase()) + (p.X + p.Y * getWidth()); + } + + const_iterator RowIterator(Vec2 p) const + { + return std::begin(getBase()) + (p.X + p.Y * getWidth()); + } + + value_type *data() + { + return std::data(getBase()); + } + + value_type const *data() const + { + return std::data(getBase()); + } + + value_type &operator[](Vec2 p) + { + return getBase()[p.X + p.Y * getWidth()]; + } + + value_type const &operator[](Vec2 p) const + { + return getBase()[p.X + p.Y * getWidth()]; + } +}; diff --git a/src/common/Platform.cpp b/src/common/Platform.cpp deleted file mode 100644 index fd91ab9c8..000000000 --- a/src/common/Platform.cpp +++ /dev/null @@ -1,577 +0,0 @@ -#include "Platform.h" - -#include -#include -#include -#include -#include -#include -#include - -#ifdef WIN -# ifndef NOMINMAX -# define NOMINMAX -# endif -# include -# include -# include -# include -# include -# include -#else -# include -# include -# include -# include -#endif -#ifdef MACOSX -# include -#endif - -#include "Misc.h" -#include "client/Client.h" - -namespace Platform -{ - -std::string originalCwd; -std::string sharedCwd; - -ByteString GetCwd() -{ - ByteString cwd; -#if defined(WIN) - wchar_t *cwdPtr = _wgetcwd(NULL, 0); - if (cwdPtr) - { - cwd = WinNarrow(cwdPtr); - } - free(cwdPtr); -#else - char *cwdPtr = getcwd(NULL, 0); - if (cwdPtr) - { - cwd = cwdPtr; - } - free(cwdPtr); -#endif - return cwd; -} - -ByteString ExecutableName() -{ - ByteString ret; -#if defined(WIN) - wchar_t *name = (wchar_t *)malloc(sizeof(wchar_t) * 64); - DWORD max = 64, res; - while ((res = GetModuleFileNameW(NULL, name, max)) >= max) - { -#elif defined MACOSX - char *fn = (char*)malloc(64),*name = (char*)malloc(PATH_MAX); - uint32_t max = 64, res; - if (_NSGetExecutablePath(fn, &max) != 0) - { - char *realloced_fn = (char*)realloc(fn, max); - assert(realloced_fn != NULL); - fn = realloced_fn; - _NSGetExecutablePath(fn, &max); - } - if (realpath(fn, name) == NULL) - { - free(fn); - free(name); - return ""; - } - res = 1; -#else - char fn[64], *name = (char *)malloc(64); - size_t max = 64, res; - sprintf(fn, "/proc/self/exe"); - memset(name, 0, max); - while ((res = readlink(fn, name, max)) >= max-1) - { -#endif -#ifndef MACOSX -#if defined(WIN) - using Char = wchar_t; -#else - using Char = char; -#endif - max *= 2; - Char* realloced_name = (Char *)realloc(name, sizeof(Char) * max); - assert(realloced_name != NULL); - name = realloced_name; - memset(name, 0, sizeof(Char) * max); - } -#endif - if (res <= 0) - { - free(name); - return ""; - } -#if defined(WIN) - ret = WinNarrow(name); -#else - ret = name; -#endif - free(name); - return ret; -} - -void DoRestart() -{ - ByteString exename = ExecutableName(); - if (exename.length()) - { -#ifdef WIN - int ret = int(INT_PTR(ShellExecuteW(NULL, NULL, WinWiden(exename).c_str(), NULL, NULL, SW_SHOWNORMAL))); - if (ret <= 32) - { - fprintf(stderr, "cannot restart: ShellExecute(...) failed: code %i\n", ret); - } - else - { -# if !defined(RENDERER) && !defined(FONTEDITOR) - Client::Ref().Shutdown(); // very ugly hack; will fix soon(tm) -# endif - exit(0); - } -#elif defined(LIN) || defined(MACOSX) - execl(exename.c_str(), exename.c_str(), NULL); - int ret = errno; - fprintf(stderr, "cannot restart: execl(...) failed: code %i\n", ret); -#endif - } - else - { - fprintf(stderr, "cannot restart: no executable name???\n"); - } - exit(-1); -} - -void OpenURI(ByteString uri) -{ -#if defined(WIN) - if (int(INT_PTR(ShellExecuteW(NULL, NULL, WinWiden(uri).c_str(), NULL, NULL, SW_SHOWNORMAL))) <= 32) - { - fprintf(stderr, "cannot open URI: ShellExecute(...) failed\n"); - } -#elif defined(MACOSX) - if (system(("open \"" + uri + "\"").c_str())) - { - fprintf(stderr, "cannot open URI: system(...) failed\n"); - } -#elif defined(LIN) - if (system(("xdg-open \"" + uri + "\"").c_str())) - { - fprintf(stderr, "cannot open URI: system(...) failed\n"); - } -#else - fprintf(stderr, "cannot open URI: not implemented\n"); -#endif -} - -void Millisleep(long int t) -{ -#ifdef WIN - Sleep(t); -#else - struct timespec s; - s.tv_sec = t / 1000; - s.tv_nsec = (t % 1000) * 10000000; - nanosleep(&s, NULL); -#endif -} - -long unsigned int GetTime() -{ -#ifdef WIN - return GetTickCount(); -#elif defined(MACOSX) - struct timeval s; - gettimeofday(&s, NULL); - return (unsigned int)(s.tv_sec * 1000 + s.tv_usec / 1000); -#else - struct timespec s; - clock_gettime(CLOCK_MONOTONIC, &s); - return s.tv_sec * 1000 + s.tv_nsec / 1000000; -#endif -} - - -void LoadFileInResource(int name, int type, unsigned int& size, const char*& data) -{ -#ifdef _MSC_VER - HMODULE handle = ::GetModuleHandle(NULL); - HRSRC rc = ::FindResource(handle, MAKEINTRESOURCE(name), MAKEINTRESOURCE(type)); - HGLOBAL rcData = ::LoadResource(handle, rc); - size = ::SizeofResource(handle, rc); - data = static_cast(::LockResource(rcData)); -#endif -} - -bool Stat(ByteString filename) -{ -#ifdef WIN - struct _stat s; - if (_stat(filename.c_str(), &s) == 0) -#else - struct stat s; - if (stat(filename.c_str(), &s) == 0) -#endif - { - return true; // Something exists, be it a file, directory, link, etc. - } - else - { - return false; // Doesn't exist - } -} - -bool FileExists(ByteString filename) -{ -#ifdef WIN - struct _stat s; - if (_stat(filename.c_str(), &s) == 0) -#else - struct stat s; - if (stat(filename.c_str(), &s) == 0) -#endif - { - if(s.st_mode & S_IFREG) - { - return true; // Is file - } - else - { - return false; // Is directory or something else - } - } - else - { - return false; // Doesn't exist - } -} - -bool DirectoryExists(ByteString directory) -{ -#ifdef WIN - struct _stat s; - if (_stat(directory.c_str(), &s) == 0) -#else - struct stat s; - if (stat(directory.c_str(), &s) == 0) -#endif - { - if(s.st_mode & S_IFDIR) - { - return true; // Is directory - } - else - { - return false; // Is file or something else - } - } - else - { - return false; // Doesn't exist - } -} - -bool RemoveFile(ByteString filename) -{ -#ifdef WIN - return _wremove(WinWiden(filename).c_str()) == 0; -#else - return remove(filename.c_str()) == 0; -#endif -} - -bool RenameFile(ByteString filename, ByteString newFilename) -{ -#ifdef WIN - return _wrename(WinWiden(filename).c_str(), WinWiden(newFilename).c_str()) == 0; -#else - return rename(filename.c_str(), newFilename.c_str()) == 0; -#endif -} - -bool DeleteDirectory(ByteString folder) -{ -#ifdef WIN - return _wrmdir(WinWiden(folder).c_str()) == 0; -#else - return rmdir(folder.c_str()) == 0; -#endif -} - -bool MakeDirectory(ByteString dir) -{ -#ifdef WIN - return _wmkdir(WinWiden(dir).c_str()) == 0; -#else - return mkdir(dir.c_str(), 0755) == 0; -#endif -} - -// Returns a list of all files in a directory matching a search -// search - list of search terms. extensions - list of extensions to also match -std::vector DirectorySearch(ByteString directory, ByteString search, std::vector extensions) -{ - //Get full file listing - //Normalise directory string, ensure / or \ is present - if (!directory.size() || (directory.back() != '/' && directory.back() != '\\')) - directory += PATH_SEP; - std::vector directoryList; -#ifdef WIN - struct _wfinddata_t currentFile; - intptr_t findFileHandle; - ByteString fileMatch = directory + "*.*"; - findFileHandle = _wfindfirst(Platform::WinWiden(fileMatch).c_str(), ¤tFile); - if (findFileHandle == -1L) - { - return std::vector(); - } - do - { - directoryList.push_back(Platform::WinNarrow(currentFile.name)); - } - while (_wfindnext(findFileHandle, ¤tFile) == 0); - _findclose(findFileHandle); -#else - struct dirent * directoryEntry; - DIR *directoryHandle = opendir(directory.c_str()); - if (!directoryHandle) - { - return std::vector(); - } - while ((directoryEntry = readdir(directoryHandle))) - { - directoryList.push_back(ByteString(directoryEntry->d_name)); - } - closedir(directoryHandle); -#endif - - search = search.ToLower(); - - std::vector searchResults; - for (std::vector::iterator iter = directoryList.begin(), end = directoryList.end(); iter != end; ++iter) - { - ByteString filename = *iter, tempfilename = *iter; - bool extensionMatch = !extensions.size(); - for (auto &extension : extensions) - { - if (filename.size() >= extension.size() && filename.EndsWith(extension)) - { - extensionMatch = true; - tempfilename = filename.SubstrFromEnd(0, extension.size()).ToLower(); - break; - } - } - bool searchMatch = !search.size(); - if (search.size() && tempfilename.Contains(search)) - searchMatch = true; - - if (searchMatch && extensionMatch) - searchResults.push_back(filename); - } - - //Filter results - return searchResults; -} - -String DoMigration(ByteString fromDir, ByteString toDir) -{ - if (fromDir.at(fromDir.length() - 1) != '/') - fromDir = fromDir + '/'; - if (toDir.at(toDir.length() - 1) != '/') - toDir = toDir + '/'; - - std::ofstream logFile(fromDir + "/migrationlog.txt", std::ios::out); - logFile << "Running migration of data from " << fromDir + " to " << toDir << std::endl; - - // Get lists of files to migrate - auto stamps = DirectorySearch(fromDir + "stamps", "", { ".stm" }); - auto saves = DirectorySearch(fromDir + "Saves", "", { ".cps", ".stm" }); - auto scripts = DirectorySearch(fromDir + "scripts", "", { ".lua", ".txt" }); - auto downloadedScripts = DirectorySearch(fromDir + "scripts/downloaded", "", { ".lua" }); - bool hasScriptinfo = FileExists(toDir + "scripts/downloaded/scriptinfo"); - auto screenshots = DirectorySearch(fromDir, "screenshot", { ".png" }); - bool hasAutorun = FileExists(fromDir + "autorun.lua"); - bool hasPref = FileExists(fromDir + "powder.pref"); - - if (stamps.empty() && saves.empty() && scripts.empty() && downloadedScripts.empty() && screenshots.empty() && !hasAutorun && !hasPref) - { - logFile << "Nothing to migrate."; - return "Nothing to migrate. This button is used to migrate data from pre-96.0 TPT installations to the shared directory"; - } - - StringBuilder result; - std::stack dirsToDelete; - - // Migrate a list of files - auto migrateList = [&](std::vector list, ByteString directory, String niceName) { - result << '\n' << niceName << ": "; - if (!list.empty() && !directory.empty()) - MakeDirectory(toDir + directory); - int migratedCount = 0, failedCount = 0; - for (auto &item : list) - { - std::string from = fromDir + directory + "/" + item; - std::string to = toDir + directory + "/" + item; - if (!FileExists(to)) - { - if (rename(from.c_str(), to.c_str())) - { - failedCount++; - logFile << "failed to move " << from << " to " << to << std::endl; - } - else - { - migratedCount++; - logFile << "moved " << from << " to " << to << std::endl; - } - } - else - { - logFile << "skipping " << from << "(already exists)" << std::endl; - } - } - - dirsToDelete.push(directory); - result << "\bt" << migratedCount << " migratated\x0E, \br" << failedCount << " failed\x0E"; - int duplicates = list.size() - migratedCount - failedCount; - if (duplicates) - result << ", " << list.size() - migratedCount - failedCount << " skipped (duplicate)"; - }; - - // Migrate a single file - auto migrateFile = [&fromDir, &toDir, &result, &logFile](ByteString filename) { - ByteString from = fromDir + filename; - ByteString to = toDir + filename; - if (!FileExists(to)) - { - if (rename(from.c_str(), to.c_str())) - { - logFile << "failed to move " << from << " to " << to << std::endl; - result << "\n\br" << filename.FromUtf8() << " migration failed\x0E"; - } - else - { - logFile << "moved " << from << " to " << to << std::endl; - result << '\n' << filename.FromUtf8() << " migrated"; - } - } - else - { - logFile << "skipping " << from << "(already exists)" << std::endl; - result << '\n' << filename.FromUtf8() << " skipped (already exists)"; - } - - if (!RemoveFile(fromDir + filename)) { - logFile << "failed to delete " << filename << std::endl; - } - }; - - // Do actual migration - RemoveFile(fromDir + "stamps/stamps.def"); - migrateList(stamps, "stamps", "Stamps"); - migrateList(saves, "Saves", "Saves"); - if (!scripts.empty()) - migrateList(scripts, "scripts", "Scripts"); - if (!hasScriptinfo && !downloadedScripts.empty()) - { - migrateList(downloadedScripts, "scripts/downloaded", "Downloaded scripts"); - migrateFile("scripts/downloaded/scriptinfo"); - } - if (!screenshots.empty()) - migrateList(screenshots, "", "Screenshots"); - if (hasAutorun) - migrateFile("autorun.lua"); - if (hasPref) - migrateFile("powder.pref"); - - // Delete leftover directories - while (!dirsToDelete.empty()) - { - ByteString toDelete = dirsToDelete.top(); - if (!DeleteDirectory(fromDir + toDelete)) { - logFile << "failed to delete " << toDelete << std::endl; - } - dirsToDelete.pop(); - } - - // chdir into the new directory - chdir(toDir.c_str()); - -#if !defined(RENDERER) && !defined(FONTEDITOR) - if (scripts.size()) - Client::Ref().RescanStamps(); -#endif - - logFile << std::endl << std::endl << "Migration complete. Results: " << result.Build().ToUtf8(); - logFile.close(); - - return result.Build(); -} - -#ifdef WIN -ByteString WinNarrow(const std::wstring &source) -{ - int buffer_size = WideCharToMultiByte(CP_UTF8, 0, source.c_str(), source.size(), nullptr, 0, NULL, NULL); - if (!buffer_size) - { - return ""; - } - std::string output(buffer_size, 0); - if (!WideCharToMultiByte(CP_UTF8, 0, source.c_str(), source.size(), &output[0], buffer_size, NULL, NULL)) - { - return ""; - } - return output; -} - -std::wstring WinWiden(const ByteString &source) -{ - int buffer_size = MultiByteToWideChar(CP_UTF8, 0, source.c_str(), source.size(), nullptr, 0); - if (!buffer_size) - { - return L""; - } - std::wstring output(buffer_size, 0); - if (!MultiByteToWideChar(CP_UTF8, 0, source.c_str(), source.size(), &output[0], buffer_size)) - { - return L""; - } - return output; -} -#endif - -bool ReadFile(std::vector &fileData, ByteString filename) -{ - std::ifstream f(filename, std::ios::binary); - 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) - { - std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl; - return false; - } - return true; -} - -bool WriteFile(std::vector fileData, ByteString filename) -{ - std::ofstream f(filename, std::ios::binary); - if (f) f.write(&fileData[0], fileData.size()); - if (!f) - { - std::cerr << "WriteFile: " << filename << ": " << strerror(errno) << std::endl; - return false; - } - return true; -} - -} diff --git a/src/common/RasterGeometry.h b/src/common/RasterGeometry.h new file mode 100644 index 000000000..b2cee21d1 --- /dev/null +++ b/src/common/RasterGeometry.h @@ -0,0 +1,164 @@ +#include "common/Vec2.h" + +// Draw a line from the origin on the ZW plane, assuming abs(dw) <= dz +template +void rasterizeLineZW(int dz, int dw, F f) +{ + const int incW = dw >= 0 ? 1 : -1; + int w = 0, err = 0; + for (int z = 0; z <= dz; z++) + { + f(z, w); + + // err / (2 * dz) is the difference between the integer w and the + // (potentially non-integer) value z * dw / dz that would like w to be. + // When the difference becomes too large, we can increment w. + err += 2 * dw * incW; + if (err >= dz) + { + w += incW; + err -= 2 * dz; + if (Ortho && z < dz) + f(z, w); + } + } +} + +// Call f for every point on the rasterization of a line between p1 and p2. +// Ortho makes the resulting line orthogonally connected. +template +void RasterizeLine(Vec2 p1, Vec2 p2, F f) +{ + if(std::abs(p1.X - p2.X) >= std::abs(p1.Y - p2.Y)) + { + // If it's more wide than tall, map Z to X and W to Y + auto source = p1.X < p2.X ? p1 : p2; + auto delta = p1.X < p2.X ? p2 - p1 : p1 - p2; + rasterizeLineZW(delta.X, delta.Y, [source, f](int z, int w) { f(source + Vec2(z, w)); }); + } + else + { + // If it's more tall than wide, map Z to Y and W to X + auto source = p1.Y < p2.Y ? p1 : p2; + auto delta = p1.Y < p2.Y ? p2 - p1 : p1 - p2; + rasterizeLineZW(delta.Y, delta.X, [source, f](int z, int w) { f(source + Vec2(w, z)); }); + } +} + +template +void rasterizeEllipseQuadrant(Vec2 radiusSquared, F f) +{ + // An ellipse is a region of points (x, y) such that + // (x / rx)^2 + (y / ry)^2 <= 1, which can be rewritten as + // x^2 * ry^2 + y^2 * rx^2 <= ry^2 * rx^2, + // except, if rx == 0, then an additional constraint abs(y) <= ry must be + // added, and same for ry. + // The code below ensures 0 <= x <= rx and 0 <= y <= ry + 1. + // A false positive for y > ry can only happen if rx == 0 and does not + // affect the outcome + auto inEllipse = [=](int x, int y) + { + return y * y * radiusSquared.X + x * x * radiusSquared.Y <= radiusSquared.X * radiusSquared.Y; + }; + // Focusing on the bottom right quadrant, in every row we find the range of + // points inside the ellipse, and within those, the range of points on the + // boundary. + int x = int(std::floor(std::sqrt(radiusSquared.X))); + int maxY = int(std::floor(std::sqrt(radiusSquared.Y))); + for (int y = 0; y <= maxY; y++) + { + // At the start of each iteration, (x, y) is on the boundary, + // i.e. the range of points inside the ellipse is [0, x] + if (inEllipse(x, y + 1)) + { + // If the point below is inside, x is the only boundary point + f(x, x, y); + } + else + { + // Otherwise, all points whose below point is outside -- are on the boundary + int xStart = x; + do + { + x--; + } while (x >= 0 && !inEllipse(x, y + 1)); + f(x + 1, xStart, y); + } + } +} + +// Call f for every point on the rasterized boundary of the ellipse with the +// indicated radius. +// In some situations we may want the radius to be the square root of an +// integer, so passing the radius squared allows for exact calculation. +// In some situations we may want the radius to be a half-integer, and floating +// point arithmetic is still exact for half-integers (really, 1/16ths), which is +// why we pass this as a float. +template +void RasterizeEllipsePoints(Vec2 radiusSquared, F f) +{ + rasterizeEllipseQuadrant(radiusSquared, [f](int x1, int x2, int y) + { + for (int x = x1; x <= x2; x++) + { + f(Vec2(x, y)); + if (x) f(Vec2(-x, y)); + if (y) f(Vec2(x, -y)); + if (x && y) f(Vec2(-x, -y)); + } + }); +} + +// Call f for every point inside the ellipse with the indicated radius. +template +void RasterizeEllipseRows(Vec2 radiusSquared, F f) +{ + rasterizeEllipseQuadrant(radiusSquared, [f](int _, int xLim, int y) + { + f(xLim, y); + if (y) f(xLim, -y); + }); +} + +// Call f for every point on the boundary of the indicated rectangle (so that +// TopLeft and BottomRight are both corners). +template +void RasterizeRect(Rect rect, F f) +{ + for (int x = rect.TopLeft.X; x <= rect.BottomRight.X; x++) + f(Vec2(x, rect.TopLeft.Y)); + + if (rect.TopLeft.Y != rect.BottomRight.Y) + for (int x = rect.TopLeft.X; x <= rect.BottomRight.X; x++) + f(Vec2(x, rect.BottomRight.Y)); + + // corners already drawn + for (int y = rect.TopLeft.Y + 1; y <= rect.BottomRight.Y - 1; y++) + f(Vec2(rect.TopLeft.X, y)); + + if (rect.TopLeft.X != rect.BottomRight.X) + for (int y = rect.TopLeft.Y + 1; y <= rect.BottomRight.Y - 1; y++) + f(Vec2(rect.BottomRight.X, y)); +} + +// Call f for every point on the dotted boundary of the indicated rectangle. +template +void RasterizeDottedRect(Rect rect, F f) +{ + for (int x = rect.TopLeft.X; x <= rect.BottomRight.X; x += 2) + f(Vec2(x, rect.TopLeft.Y)); + + int bottomOff = (rect.BottomRight.Y - rect.TopLeft.Y) % 2; + if (rect.TopLeft.Y != rect.BottomRight.Y) + for (int x = rect.TopLeft.X + bottomOff; x <= rect.BottomRight.X; x += 2) + f(Vec2(x, rect.BottomRight.Y)); + + // corners already drawn + for (int y = rect.TopLeft.Y + 1 + 1; y <= rect.BottomRight.Y - 1; y += 2) + f(Vec2(rect.TopLeft.X, y)); + + int leftOff = (rect.BottomRight.X - rect.TopLeft.X + 1) % 2; + if (rect.TopLeft.X != rect.BottomRight.X) + for (int y = rect.TopLeft.Y + 1 + leftOff; y <= rect.BottomRight.Y - 1; y += 2) + f(Vec2(rect.BottomRight.X, y)); +} diff --git a/src/common/Singleton.h b/src/common/Singleton.h deleted file mode 100644 index 6b2214eb3..000000000 --- a/src/common/Singleton.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef SINGLETON_H -#define SINGLETON_H - -template - -class Singleton -{ -public: - static T& Ref() - { - static T instance; - return instance; - } -}; - -#endif // SINGLETON_H diff --git a/src/common/String.h b/src/common/String.h index b15c7422c..9cbea581b 100644 --- a/src/common/String.h +++ b/src/common/String.h @@ -1,6 +1,5 @@ #pragma once -#include "Config.h" - +#include "tpt-minmax.h" #include #include #include @@ -9,8 +8,6 @@ #include #include -#include "tpt-minmax.h" - /* There are two "string" classes: ByteString and String. They have nearly identical interfaces, except that one stores 8-bit octets (bytes) and diff --git a/src/common/VariantIndex.h b/src/common/VariantIndex.h new file mode 100644 index 000000000..4544fcd50 --- /dev/null +++ b/src/common/VariantIndex.h @@ -0,0 +1,22 @@ +#pragma once +#include +#include + +// https://stackoverflow.com/a/52303671 +template +constexpr std::size_t VariantIndex() +{ + static_assert(std::variant_size_v > index, "Type not found in variant"); + if constexpr (index == std::variant_size_v) + { + return index; + } + else if constexpr (std::is_same_v, T>) + { + return index; + } + else + { + return VariantIndex(); + } +} diff --git a/src/common/Vec2.h b/src/common/Vec2.h new file mode 100644 index 000000000..c0ad17c5f --- /dev/null +++ b/src/common/Vec2.h @@ -0,0 +1,455 @@ +#pragma once + +#include +#include +#include +#include +#include + +template>> +struct Rect; + +template>> +struct Vec2 +{ + T X, Y; + + constexpr Vec2(T x, T y): + X(x), + Y(y) + { + } + + template>> + constexpr explicit Vec2(Vec2 other): + X(other.X), + Y(other.Y) + { + } + + constexpr bool operator==(Vec2 other) const + { + return X == other.X && Y == other.Y; + } + + constexpr bool operator!=(Vec2 other) const + { + return X != other.X || Y != other.Y; + } + + template + constexpr Vec2() + std::declval())> operator+(Vec2 other) const + { + return Vec2() + std::declval())>(X + other.X, Y + other.Y); + } + + constexpr Vec2 operator-() const + { + return Vec2(-X, -Y); + } + + template + constexpr Vec2() - std::declval())> operator-(Vec2 other) const + { + return Vec2() - std::declval())>(X - other.X, Y - other.Y); + } + + template>> + constexpr Vec2() * std::declval())> operator*(S other) const + { + return Vec2() * std::declval())>(X * other, Y * other); + } + + template>> + constexpr Vec2() / std::declval())> operator/(S other) const + { + return Vec2() / std::declval())>(X / other, Y / other); + } + + template + constexpr Vec2 &operator+=(Vec2 other) + { + return *this = *this + other; + } + + template + constexpr Vec2 &operator-=(Vec2 other) + { + return *this = *this - other; + } + + template + constexpr Vec2 &operator*=(Vec2 other) + { + return *this = *this * other; + } + + template + constexpr Vec2 &operator/=(Vec2 other) + { + return *this = *this / other; + } + + // Round towards -infinity + template>> + Vec2 Floor() const + { + return Vec2(std::floor(X), std::floor(Y)); + } + + // Round towards nearest integer, halfpoints towards -infinity + template>> + Vec2 Round() const + { + return (*this + Vec2(0.5, 0.5)).Floor(); + } + + Vec2 Clamp(Rect rect) const + { + return Vec2( + std::clamp(X, rect.TopLeft.X, rect.BottomRight.X), + std::clamp(Y, rect.TopLeft.Y, rect.BottomRight.Y) + ); + } + + // Return a rectangle starting at origin, whose dimensions match this vector + template>> + constexpr inline Rect OriginRect() const + { + return RectSized(Vec2(0, 0), *this); + } + + static Vec2 const Zero; +}; + +template +Vec2 const Vec2::Zero = Vec2(0, 0); + +template>> +struct Mat2 +{ + // ⎛A B⎞ + // ⎝C D⎠, acting on column vectors + T A, B, C, D; + + constexpr Mat2(T a, T b, T c, T d): + A(a), + B(b), + C(c), + D(d) + { + } + + constexpr bool operator==(Mat2 other) const + { + return A == other.A && B == other.B && C == other.C && D == other.D; + } + + constexpr bool operator!=(Mat2 other) const + { + return A != other.A || B != other.B || C != other.C || D != other.D; + } + + template + constexpr Vec2() * std::declval())> operator*(Vec2 vec) const + { + return Vec2() * std::declval())>(A * vec.X + B * vec.Y, C * vec.X + D * vec.Y); + } + + template + constexpr Mat2() * std::declval())> operator*(Mat2 mat) const + { + return Mat2() * std::declval())>( + A * mat.A + B * mat.C, A * mat.B + B * mat.D, + C * mat.A + D * mat.C, C * mat.B + D * mat.D + ); + } + + static Mat2 const Identity, MirrorX, MirrorY, CCW; +}; + +template +Mat2 const Mat2::Identity = Mat2(1, 0, 0, 1); +template +Mat2 const Mat2::MirrorX = Mat2(-1, 0, 0, 1); +template +Mat2 const Mat2::MirrorY = Mat2(1, 0, 0, -1); +template +Mat2 const Mat2::CCW = Mat2(0, 1, -1, 0); // reminder: the Y axis points down + +template>> +constexpr static inline Rect RectBetween(Vec2, Vec2); + +enum IterationDirection +{ + TOP_TO_BOTTOM, + BOTTOM_TO_TOP, + LEFT_TO_RIGHT, + RIGHT_TO_LEFT, +}; + +template +struct Rect +{ + // Inclusive + Vec2 TopLeft, BottomRight; + +private: + constexpr Rect(Vec2 topLeft, Vec2 bottomRight): + TopLeft(topLeft), + BottomRight(bottomRight) + { + } + friend constexpr Rect RectBetween(Vec2, Vec2); + + struct end_sentinel + {}; + + template + struct range_row_major + { + static_assert(D1 == TOP_TO_BOTTOM || D1 == BOTTOM_TO_TOP); + static_assert(D2 == LEFT_TO_RIGHT || D2 == RIGHT_TO_LEFT); + T left, top, right, bottom; + + struct iterator + { + T x, y; + T const first_x, last_x, end_y; + + iterator &operator++() + { + if (x == last_x) + { + x = first_x; + if constexpr (D1 == TOP_TO_BOTTOM) + y++; + else + y--; + } + else + { + if constexpr (D2 == LEFT_TO_RIGHT) + x++; + else + x--; + } + return *this; + } + + Vec2 operator*() const + { + return Vec2(x, y); + } + + bool operator!=(end_sentinel) const + { + if constexpr (D1 == TOP_TO_BOTTOM) + return y < end_y; + else + return y > end_y; + } + + using difference_type = void; + using value_type = Vec2; + using pointer = void; + using reference = void; + using iterator_category = std::forward_iterator_tag; + }; + + iterator begin() const + { + T first_x = D2 == LEFT_TO_RIGHT ? left : right; + T last_x = D2 == LEFT_TO_RIGHT ? right : left; + T first_y = D1 == TOP_TO_BOTTOM ? top : bottom; + T end_y = D1 == TOP_TO_BOTTOM ? bottom + 1 : top - 1; + return iterator{first_x, right >= left ? first_y : end_y, first_x, last_x, end_y}; + } + + end_sentinel end() const + { + return end_sentinel(); + } + }; + + template + struct range_column_major + { + static_assert(D1 == LEFT_TO_RIGHT || D1 == RIGHT_TO_LEFT); + static_assert(D2 == TOP_TO_BOTTOM || D2 == BOTTOM_TO_TOP); + T left, top, right, bottom; + + struct iterator + { + T x, y; + T const first_y, last_y, end_x; + + iterator &operator++() + { + if (y == last_y) + { + y = first_y; + if constexpr (D1 == LEFT_TO_RIGHT) + x++; + else + x--; + } + else + { + if constexpr (D2 == TOP_TO_BOTTOM) + y++; + else + y--; + } + return *this; + } + + Vec2 operator*() const + { + return Vec2(x, y); + } + + bool operator!=(end_sentinel) const + { + if constexpr (D1 == LEFT_TO_RIGHT) + return x < end_x; + else + return x > end_x; + } + + using difference_type = void; + using value_type = Vec2; + using pointer = void; + using reference = void; + using iterator_category = std::forward_iterator_tag; + }; + + iterator begin() const + { + T first_y = D2 == TOP_TO_BOTTOM ? top : bottom; + T last_y = D2 == TOP_TO_BOTTOM ? bottom : top; + T first_x = D1 == LEFT_TO_RIGHT ? left : right; + T end_x = D1 == LEFT_TO_RIGHT ? right + 1 : left - 1; + return iterator{bottom >= top ? first_x : end_x, first_y, first_y, last_y, end_x}; + } + + end_sentinel end() const + { + return end_sentinel(); + } + }; + +public: + constexpr bool operator==(Rect other) const + { + return TopLeft == other.TopLeft && BottomRight == other.BottomRight; + } + + constexpr bool operator!=(Rect other) const + { + return TopLeft != other.TopLeft || BottomRight != other.BottomRight; + } + + constexpr explicit operator bool() const + { + return BottomRight.X >= TopLeft.X && BottomRight.Y >= TopLeft.Y; + } + + // Return the smallest rectangle that contains both input rectangles, + // **assuming neither are empty** + Rect operator|(Rect other) const + { + return Rect( + Vec2(std::min(TopLeft.X, other.TopLeft.X), std::min(TopLeft.Y, other.TopLeft.Y)), + Vec2(std::max(BottomRight.X, other.BottomRight.X), std::max(BottomRight.Y, other.BottomRight.Y)) + ); + } + + // Return the intersection of two rectangles (possibly empty) + Rect operator&(Rect other) const + { + auto rect = Rect( + Vec2(std::max(TopLeft.X, other.TopLeft.X), std::max(TopLeft.Y, other.TopLeft.Y)), + Vec2(std::min(BottomRight.X, other.BottomRight.X), std::min(BottomRight.Y, other.BottomRight.Y)) + ); + return Rect( + rect.TopLeft, + Vec2( + std::max(rect.TopLeft.X - 1, rect.BottomRight.X), + std::max(rect.TopLeft.Y - 1, rect.BottomRight.Y) + ) + ); + } + + inline Rect &operator|=(Rect other) + { + return *this = *this | other; + } + + inline Rect &operator&=(Rect other) + { + return *this = *this & other; + } + + inline bool Contains(Vec2 point) const + { + return point.X >= TopLeft.X && point.X <= BottomRight.X && point.Y >= TopLeft.Y && point.Y <= BottomRight.Y; + } + + template>> + inline Vec2 Size() const + { + return BottomRight - TopLeft + Vec2(1, 1); + } + + template + Rect() + std::declval())> Inset(S delta) const + { + return Rect() + std::declval())>(TopLeft + Vec2(delta, delta), BottomRight - Vec2(delta, delta)); + } + + template>> + constexpr auto Range() const + { + static_assert( + ((D1 == TOP_TO_BOTTOM || D1 == BOTTOM_TO_TOP) && (D2 == LEFT_TO_RIGHT || D2 == RIGHT_TO_LEFT)) || + ((D1 == LEFT_TO_RIGHT || D1 == RIGHT_TO_LEFT) && (D2 == TOP_TO_BOTTOM || D2 == BOTTOM_TO_TOP)), + "Must include exactly 1 of TOP_TO_BOTTOM/BOTTOM_TO_TOP and exactly 1 of LEFT_TO_RIGHT/RIGHT_TO_LEFT" + ); + if constexpr (D1 == TOP_TO_BOTTOM || D1 == BOTTOM_TO_TOP) + { + return range_row_major{TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y}; + } + else + return range_column_major{TopLeft.X, TopLeft.Y, BottomRight.X, BottomRight.Y}; + } + + // Use when the order isn't important + constexpr typename range_row_major::iterator begin() const + { + return Range().begin(); + } + + constexpr end_sentinel end() const + { + return end_sentinel(); + } +}; + +template +constexpr inline Rect RectBetween(Vec2 topLeft, Vec2 bottomRight) +{ + return Rect(topLeft, bottomRight); +} + +template>> +constexpr inline Rect RectAt(Vec2 pos) +{ + return RectBetween(pos, pos); +} + +template>> +constexpr inline Rect RectSized(Vec2 topLeft, Vec2 dimen) +{ + return RectBetween(topLeft, topLeft + dimen - Vec2(1, 1)); +} diff --git a/src/common/macosx.h b/src/common/macosx.h deleted file mode 100644 index d902c1ad0..000000000 --- a/src/common/macosx.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#ifdef DEBUG -# undef DEBUG -# define DEBUG 1 -#else -# define DEBUG 0 -#endif -#include -#include diff --git a/src/common/meson.build b/src/common/meson.build index 1228599aa..5f6c63294 100644 --- a/src/common/meson.build +++ b/src/common/meson.build @@ -1,6 +1,7 @@ common_files += files( - 'Platform.cpp', 'String.cpp', 'tpt-rand.cpp', 'tpt-thread-local.cpp', ) + +subdir('platform') diff --git a/src/common/platform/Android.cpp b/src/common/platform/Android.cpp new file mode 100644 index 000000000..e2599ceff --- /dev/null +++ b/src/common/platform/Android.cpp @@ -0,0 +1,27 @@ +#include "Platform.h" +#include + +namespace Platform +{ +void OpenURI(ByteString uri) +{ + fprintf(stderr, "cannot open URI: not implemented\n"); +} + +long unsigned int GetTime() +{ + struct timespec s; + clock_gettime(CLOCK_MONOTONIC, &s); + return s.tv_sec * 1000 + s.tv_nsec / 1000000; +} + +ByteString ExecutableNameFirstApprox() +{ + return "/proc/self/exe"; +} + +bool CanUpdate() +{ + return false; +} +} diff --git a/src/common/platform/Common.cpp b/src/common/platform/Common.cpp new file mode 100644 index 000000000..2b15b4d47 --- /dev/null +++ b/src/common/platform/Common.cpp @@ -0,0 +1,127 @@ +#include "Platform.h" +#include "resource.h" +#include "common/tpt-rand.h" +#include "Config.h" +#include +#include +#include +#include +#include +#include + +namespace Platform +{ + +std::string originalCwd; +std::string sharedCwd; + +// Returns a list of all files in a directory matching a search +// search - list of search terms. extensions - list of extensions to also match +std::vector DirectorySearch(ByteString directory, ByteString search, std::vector extensions) +{ + //Get full file listing + //Normalise directory string, ensure / or \ is present + if (!directory.size() || (directory.back() != '/' && directory.back() != '\\')) + directory.append(1, PATH_SEP_CHAR); + auto directoryList = DirectoryList(directory); + + search = search.ToLower(); + + std::vector searchResults; + for (std::vector::iterator iter = directoryList.begin(), end = directoryList.end(); iter != end; ++iter) + { + ByteString filename = *iter, tempfilename = *iter; + bool extensionMatch = !extensions.size(); + for (auto &extension : extensions) + { + if (filename.size() >= extension.size() && filename.EndsWith(extension)) + { + extensionMatch = true; + tempfilename = filename.SubstrFromEnd(0, extension.size()).ToLower(); + break; + } + } + bool searchMatch = !search.size(); + if (search.size() && tempfilename.Contains(search)) + searchMatch = true; + + if (searchMatch && extensionMatch) + searchResults.push_back(filename); + } + + //Filter results + return searchResults; +} + +bool ReadFile(std::vector &fileData, ByteString filename) +{ + std::ifstream f(filename, std::ios::binary); + 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) + { + std::cerr << "ReadFile: " << filename << ": " << strerror(errno) << std::endl; + return false; + } + return true; +} + +bool WriteFile(const std::vector &fileData, ByteString filename) +{ + auto replace = FileExists(filename); + auto writeFileName = filename; + if (replace) + { + while (true) + { + writeFileName = ByteString::Build(filename, ".temp.", Format::Width(5), Format::Fill('0'), interfaceRng() % 100000); + if (!FileExists(writeFileName)) + { + break; + } + } + } + bool ok = false; + { + std::ofstream f(writeFileName, std::ios::binary); + if (f) f.write(&fileData[0], fileData.size()); + ok = bool(f); + } + if (!ok) + { + std::cerr << "WriteFile: " << filename << ": " << strerror(errno) << std::endl; + if (replace) + { + RemoveFile(writeFileName); + } + return false; + } + if (replace) + { + if (!RenameFile(writeFileName, filename, true)) + { + RemoveFile(writeFileName); + return false; + } + } + return true; +} + +std::list exitFuncs; + +void Atexit(ExitFunc exitFunc) +{ + exitFuncs.push_front(exitFunc); +} + +void Exit(int code) +{ + for (auto exitFunc : exitFuncs) + { + exitFunc(); + } + exit(code); +} +} diff --git a/src/common/platform/Darwin.cpp b/src/common/platform/Darwin.cpp new file mode 100644 index 000000000..b73a675aa --- /dev/null +++ b/src/common/platform/Darwin.cpp @@ -0,0 +1,51 @@ +#include "Platform.h" +#include +#include +#include +#include + +namespace Platform +{ + +void OpenURI(ByteString uri) +{ + if (system(("open \"" + uri + "\"").c_str())) + { + fprintf(stderr, "cannot open URI: system(...) failed\n"); + } +} + +long unsigned int GetTime() +{ + struct timeval s; + gettimeofday(&s, NULL); + return (unsigned int)(s.tv_sec * 1000 + s.tv_usec / 1000); +} + +ByteString ExecutableNameFirstApprox() +{ + ByteString firstApproximation("?"); + { + auto bufSize = uint32_t(firstApproximation.size()); + auto ret = _NSGetExecutablePath(&firstApproximation[0], &bufSize); + if (ret == -1) + { + // Buffer not large enough; likely to happen since it's initially a single byte. + firstApproximation.resize(bufSize); + ret = _NSGetExecutablePath(&firstApproximation[0], &bufSize); + } + if (ret != 0) + { + // Can't even get a first approximation. + std::cerr << "_NSGetExecutablePath: " << ret << std::endl; + return ""; + } + } + return firstApproximation; +} + +bool CanUpdate() +{ + return false; +} +} diff --git a/src/common/platform/Linux.cpp b/src/common/platform/Linux.cpp new file mode 100644 index 000000000..b2e87f7fe --- /dev/null +++ b/src/common/platform/Linux.cpp @@ -0,0 +1,112 @@ +#include "Platform.h" +#include "icon_cps.png.h" +#include "icon_exe.png.h" +#include "save.xml.h" +#include "powder.desktop.h" +#include "Config.h" +#include +#include + +namespace Platform +{ +void OpenURI(ByteString uri) +{ + if (system(("xdg-open \"" + uri + "\"").c_str())) + { + fprintf(stderr, "cannot open URI: system(...) failed\n"); + } +} + +long unsigned int GetTime() +{ + struct timespec s; + clock_gettime(CLOCK_MONOTONIC, &s); + return s.tv_sec * 1000 + s.tv_nsec / 1000000; +} + +ByteString ExecutableNameFirstApprox() +{ + return "/proc/self/exe"; +} + +bool CanUpdate() +{ + return true; +} + +bool Install() +{ + bool ok = true; + auto desktopEscapeString = [](ByteString str) { + ByteString escaped; + for (auto ch : str) + { + auto from = " " "\n" "\t" "\r" "\\"; + auto to = "s" "n" "t" "r" "\\"; + if (auto off = strchr(from, ch)) + { + escaped.append(1, '\\'); + escaped.append(1, to[off - from]); + } + else + { + escaped.append(1, ch); + } + } + return escaped; + }; + auto desktopEscapeExec = [](ByteString str) { + ByteString escaped; + for (auto ch : str) + { + if (strchr(" \t\n\"\'\\><~|&;$*?#()`", ch)) + { + escaped.append(1, '\\'); + } + escaped.append(1, ch); + } + return escaped; + }; + + if (ok) + { + ByteString desktopData(powder_desktop, powder_desktop + powder_desktop_size); + auto exe = Platform::ExecutableName(); + auto path = exe.SplitFromEndBy('/').Before(); + desktopData = desktopData.Substitute("Exec=" + ByteString(APPEXE), "Exec=" + desktopEscapeString(desktopEscapeExec(exe))); + desktopData += ByteString::Build("Path=", desktopEscapeString(path), "\n"); + ByteString file = ByteString::Build(APPVENDOR, "-", APPID, ".desktop"); + ok = ok && Platform::WriteFile(std::vector(desktopData.begin(), desktopData.end()), file); + ok = ok && !system(ByteString::Build("xdg-desktop-menu install ", file).c_str()); + ok = ok && !system(ByteString::Build("xdg-mime default ", file, " application/vnd.powdertoy.save").c_str()); + ok = ok && !system(ByteString::Build("xdg-mime default ", file, " x-scheme-handler/ptsave").c_str()); + Platform::RemoveFile(file); + } + if (ok) + { + ByteString file = ByteString(APPVENDOR) + "-save.xml"; + ok = ok && Platform::WriteFile(std::vector(save_xml, save_xml + save_xml_size), file); + ok = ok && !system(ByteString::Build("xdg-mime install ", file).c_str()); + Platform::RemoveFile(file); + } + if (ok) + { + ByteString file = ByteString(APPVENDOR) + "-cps.png"; + ok = ok && Platform::WriteFile(std::vector(icon_cps_png, icon_cps_png + icon_cps_png_size), file); + ok = ok && !system(ByteString::Build("xdg-icon-resource install --noupdate --context mimetypes --size 64 ", file, " application-vnd.powdertoy.save").c_str()); + Platform::RemoveFile(file); + } + if (ok) + { + ByteString file = ByteString(APPVENDOR) + "-exe.png"; + ok = ok && Platform::WriteFile(std::vector(icon_exe_png, icon_exe_png + icon_exe_png_size), file); + ok = ok && !system(ByteString::Build("xdg-icon-resource install --noupdate --size 64 ", file, " ", APPVENDOR, "-", APPEXE).c_str()); + Platform::RemoveFile(file); + } + if (ok) + { + ok = ok && !system("xdg-icon-resource forceupdate"); + } + return ok; +} +} diff --git a/src/common/platform/Null.cpp b/src/common/platform/Null.cpp new file mode 100644 index 000000000..96cd3a4cd --- /dev/null +++ b/src/common/platform/Null.cpp @@ -0,0 +1,14 @@ +#include "Platform.h" + +namespace Platform +{ +void OpenURI(ByteString uri) +{ + fprintf(stderr, "cannot open URI: not implemented\n"); +} + +bool CanUpdate() +{ + return false; +} +} diff --git a/src/common/Platform.h b/src/common/platform/Platform.h similarity index 54% rename from src/common/Platform.h rename to src/common/platform/Platform.h index e39ddce42..272555242 100644 --- a/src/common/Platform.h +++ b/src/common/platform/Platform.h @@ -1,16 +1,11 @@ -#ifndef PLATFORM_H -#define PLATFORM_H -#include "Config.h" - +#pragma once #include "common/String.h" - -#ifdef WIN -# include -#endif +#include namespace Platform { ByteString GetCwd(); + ByteString ExecutableNameFirstApprox(); ByteString ExecutableName(); void DoRestart(); @@ -19,16 +14,15 @@ namespace Platform void Millisleep(long int t); long unsigned int GetTime(); - void LoadFileInResource(int name, int type, unsigned int& size, const char*& data); - bool Stat(ByteString filename); bool FileExists(ByteString filename); bool DirectoryExists(ByteString directory); + bool IsLink(ByteString path); /** * @return true on success */ bool RemoveFile(ByteString filename); - bool RenameFile(ByteString filename, ByteString newFilename); + bool RenameFile(ByteString filename, ByteString newFilename, bool replace); /** * @return true on success @@ -39,19 +33,33 @@ namespace Platform * @return true on success */ bool MakeDirectory(ByteString dir); + std::vector DirectoryList(ByteString directory); std::vector DirectorySearch(ByteString directory, ByteString search, std::vector extensions); - String DoMigration(ByteString fromDir, ByteString toDir); bool ReadFile(std::vector &fileData, ByteString filename); - bool WriteFile(std::vector fileData, ByteString filename); + bool WriteFile(const std::vector &fileData, ByteString filename); -#ifdef WIN + // TODO: Remove these and switch to *A Win32 API variants when we stop fully supporting windows + // versions older than win10 1903, for example when win10 reaches EOL, see 18084d5aa0e5. ByteString WinNarrow(const std::wstring &source); std::wstring WinWiden(const ByteString &source); -#endif extern std::string originalCwd; extern std::string sharedCwd; -} -#endif + bool CanUpdate(); + + bool Install(); + + bool ChangeDir(ByteString toDir); + + bool UpdateStart(const std::vector &data); + bool UpdateFinish(); + void UpdateCleanup(); + + void SetupCrt(); + + using ExitFunc = void (*)(); + void Atexit(ExitFunc exitFunc); + void Exit(int code); +} diff --git a/src/common/platform/Posix.cpp b/src/common/platform/Posix.cpp new file mode 100644 index 000000000..b514ce991 --- /dev/null +++ b/src/common/platform/Posix.cpp @@ -0,0 +1,225 @@ +#include "Platform.h" +#include +#include +#include +#include +#include +#include +#include + +namespace Platform +{ +ByteString GetCwd() +{ + ByteString cwd; + char *cwdPtr = getcwd(NULL, 0); + if (cwdPtr) + { + cwd = cwdPtr; + } + free(cwdPtr); + return cwd; +} + +void Millisleep(long int t) +{ + struct timespec s; + s.tv_sec = t / 1000; + s.tv_nsec = (t % 1000) * 10000000; + nanosleep(&s, NULL); +} + +bool Stat(ByteString filename) +{ + struct stat s; + if (stat(filename.c_str(), &s) == 0) + { + return true; // Something exists, be it a file, directory, link, etc. + } + else + { + return false; // Doesn't exist + } +} + +bool FileExists(ByteString filename) +{ + struct stat s; + if (stat(filename.c_str(), &s) == 0) + { + if(s.st_mode & S_IFREG) + { + return true; // Is file + } + else + { + return false; // Is directory or something else + } + } + else + { + return false; // Doesn't exist + } +} + +bool DirectoryExists(ByteString directory) +{ + struct stat s; + if (stat(directory.c_str(), &s) == 0) + { + if(s.st_mode & S_IFDIR) + { + return true; // Is directory + } + else + { + return false; // Is file or something else + } + } + else + { + return false; // Doesn't exist + } +} + +bool IsLink(ByteString path) +{ + struct stat s; + if (stat(path.c_str(), &s) == 0) + { + if (s.st_mode & S_IFLNK) + { + return true; // Is path + } + else + { + return false; // Is file or something else + } + } + else + { + return false; // Doesn't exist + } +} + +bool RemoveFile(ByteString filename) +{ + return remove(filename.c_str()) == 0; +} + +bool RenameFile(ByteString filename, ByteString newFilename, bool replace) +{ + // TODO: Make atomic :( Could use renameat2 with RENAME_NOREPLACE on linux and + // renamex_np with RENAME_EXCL on darwin, but both require filesystem support; + // I don't think it's worth it for now. -- LBPHacker + if (!replace && FileExists(newFilename)) + { + return false; + } + return rename(filename.c_str(), newFilename.c_str()) == 0; +} + +bool DeleteDirectory(ByteString folder) +{ + return rmdir(folder.c_str()) == 0; +} + +bool MakeDirectory(ByteString dir) +{ + return mkdir(dir.c_str(), 0755) == 0; +} + +bool ChangeDir(ByteString toDir) +{ + return chdir(toDir.c_str()) == 0; +} + +std::vector DirectoryList(ByteString directory) +{ + std::vector directoryList; + struct dirent * directoryEntry; + DIR *directoryHandle = opendir(directory.c_str()); + if (!directoryHandle) + { + return std::vector(); + } + while ((directoryEntry = readdir(directoryHandle))) + { + directoryList.push_back(ByteString(directoryEntry->d_name)); + } + closedir(directoryHandle); + return directoryList; +} + +ByteString ExecutableName() +{ + auto firstApproximation = ExecutableNameFirstApprox(); + auto rp = std::unique_ptr(realpath(&firstApproximation[0], NULL), std::free); + if (!rp) + { + std::cerr << "realpath: " << errno << std::endl; + return ""; + } + return rp.get(); +} + +void DoRestart() +{ + ByteString exename = ExecutableName(); + if (exename.length()) + { + execl(exename.c_str(), exename.c_str(), NULL); + int ret = errno; + fprintf(stderr, "cannot restart: execl(...) failed: code %i\n", ret); + } + else + { + fprintf(stderr, "cannot restart: no executable name???\n"); + } + Exit(-1); +} + +bool UpdateStart(const std::vector &data) +{ + ByteString exeName = Platform::ExecutableName(); + + if (!exeName.length()) + return false; + + auto updName = exeName + "-update"; + + if (!WriteFile(data, updName)) + { + RemoveFile(updName); + return false; + } + + if (chmod(updName.c_str(), 0755)) + { + RemoveFile(updName); + return false; + } + + if (!RenameFile(updName, exeName, true)) + { + RemoveFile(updName); + return false; + } + + execl(exeName.c_str(), "powder-update", NULL); + return false; // execl returned, we failed +} + +bool UpdateFinish() +{ + return true; +} + +void UpdateCleanup() +{ +} + +void SetupCrt() +{ +} +} diff --git a/src/common/platform/Windows.cpp b/src/common/platform/Windows.cpp new file mode 100644 index 000000000..983c5ba2b --- /dev/null +++ b/src/common/platform/Windows.cpp @@ -0,0 +1,415 @@ +#include "Platform.h" +#include "resource.h" +#include "Config.h" +#ifndef NOMINMAX +# define NOMINMAX +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Platform +{ +ByteString GetCwd() +{ + ByteString cwd; + auto cwdPtr = std::unique_ptr(_wgetcwd(NULL, 0), free); + if (cwdPtr) + { + cwd = WinNarrow(cwdPtr.get()); + } + return cwd; +} + +void OpenURI(ByteString uri) +{ + if (int(INT_PTR(ShellExecuteW(NULL, NULL, WinWiden(uri).c_str(), NULL, NULL, SW_SHOWNORMAL))) <= 32) + { + fprintf(stderr, "cannot open URI: ShellExecute(...) failed\n"); + } +} + +void Millisleep(long int t) +{ + Sleep(t); +} + +long unsigned int GetTime() +{ + return GetTickCount(); +} + +bool Stat(ByteString filename) +{ + struct _stat s; + if (_wstat(WinWiden(filename).c_str(), &s) == 0) + { + return true; // Something exists, be it a file, directory, link, etc. + } + else + { + return false; // Doesn't exist + } +} + +bool FileExists(ByteString filename) +{ + struct _stat s; + if (_wstat(WinWiden(filename).c_str(), &s) == 0) + { + if(s.st_mode & S_IFREG) + { + return true; // Is file + } + else + { + return false; // Is directory or something else + } + } + else + { + return false; // Doesn't exist + } +} + +bool DirectoryExists(ByteString directory) +{ + struct _stat s; + if (_wstat(WinWiden(directory).c_str(), &s) == 0) + { + if(s.st_mode & S_IFDIR) + { + return true; // Is directory + } + else + { + return false; // Is file or something else + } + } + else + { + return false; // Doesn't exist + } +} + +bool IsLink(ByteString path) +{ + struct _stat s; + if (_wstat(WinWiden(path).c_str(), &s) == 0) + { + if (GetFileAttributesW(WinWiden(path).c_str()) & FILE_ATTRIBUTE_REPARSE_POINT) + { + return true; // Is directory + } + else + { + return false; // Is file or something else + } + } + else + { + return false; // Doesn't exist + } +} + +bool RemoveFile(ByteString filename) +{ + return _wremove(WinWiden(filename).c_str()) == 0; +} + +bool RenameFile(ByteString filename, ByteString newFilename, bool replace) +{ + if (replace) + { + // TODO: we rely on errno but errors from this are available through GetLastError(); fix + return MoveFileExW(WinWiden(filename).c_str(), WinWiden(newFilename).c_str(), MOVEFILE_REPLACE_EXISTING); + } + return _wrename(WinWiden(filename).c_str(), WinWiden(newFilename).c_str()) == 0; +} + +bool DeleteDirectory(ByteString folder) +{ + return _wrmdir(WinWiden(folder).c_str()) == 0; +} + +bool MakeDirectory(ByteString dir) +{ + return _wmkdir(WinWiden(dir).c_str()) == 0; +} + +bool ChangeDir(ByteString toDir) +{ + return _wchdir(WinWiden(toDir).c_str()) == 0; +} + +std::vector DirectoryList(ByteString directory) +{ + std::vector directoryList; + struct _wfinddata_t currentFile; + intptr_t findFileHandle; + ByteString fileMatch = directory + "*.*"; + findFileHandle = _wfindfirst(Platform::WinWiden(fileMatch).c_str(), ¤tFile); + if (findFileHandle == -1L) + { + return std::vector(); + } + do + { + directoryList.push_back(Platform::WinNarrow(currentFile.name)); + } + while (_wfindnext(findFileHandle, ¤tFile) == 0); + _findclose(findFileHandle); + return directoryList; +} + +ByteString WinNarrow(const std::wstring &source) +{ + int buffer_size = WideCharToMultiByte(CP_UTF8, 0, source.c_str(), source.size(), nullptr, 0, NULL, NULL); + if (!buffer_size) + { + return ""; + } + std::string output(buffer_size, 0); + if (!WideCharToMultiByte(CP_UTF8, 0, source.c_str(), source.size(), &output[0], buffer_size, NULL, NULL)) + { + return ""; + } + return output; +} + +std::wstring WinWiden(const ByteString &source) +{ + int buffer_size = MultiByteToWideChar(CP_UTF8, 0, source.c_str(), source.size(), nullptr, 0); + if (!buffer_size) + { + return L""; + } + std::wstring output(buffer_size, 0); + if (!MultiByteToWideChar(CP_UTF8, 0, source.c_str(), source.size(), &output[0], buffer_size)) + { + return L""; + } + return output; +} + +ByteString ExecutableName() +{ + std::wstring buf(L"?"); + while (true) + { + SetLastError(ERROR_SUCCESS); + if (!GetModuleFileNameW(NULL, &buf[0], DWORD(buf.size()))) + { + std::cerr << "GetModuleFileNameW: " << GetLastError() << std::endl; + return ""; + } + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) + { + break; + } + buf.resize(buf.size() * 2); + } + return WinNarrow(&buf[0]); // Pass pointer to copy only up to the zero terminator. +} + +void DoRestart() +{ + ByteString exename = ExecutableName(); + if (exename.length()) + { + int ret = int(INT_PTR(ShellExecuteW(NULL, NULL, WinWiden(exename).c_str(), NULL, NULL, SW_SHOWNORMAL))); + if (ret <= 32) + { + fprintf(stderr, "cannot restart: ShellExecute(...) failed: code %i\n", ret); + } + else + { + Exit(0); + } + } + else + { + fprintf(stderr, "cannot restart: no executable name???\n"); + } + Exit(-1); +} + +bool CanUpdate() +{ + return true; +} + +bool Install() +{ + bool ok = true; + auto deleteKey = [](ByteString path) { + RegDeleteKeyW(HKEY_CURRENT_USER, Platform::WinWiden(path).c_str()); + }; + auto createKey = [](ByteString path, ByteString value, ByteString extraKey = {}, ByteString extraValue = {}) { + auto ok = true; + auto wPath = Platform::WinWiden(path); + auto wValue = Platform::WinWiden(value); + auto wExtraKey = Platform::WinWiden(extraKey); + auto wExtraValue = Platform::WinWiden(extraValue); + HKEY k; + ok = ok && RegCreateKeyExW(HKEY_CURRENT_USER, wPath.c_str(), 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &k, NULL) == ERROR_SUCCESS; + ok = ok && RegSetValueExW(k, NULL, 0, REG_SZ, reinterpret_cast(wValue.c_str()), (wValue.size() + 1) * 2) == ERROR_SUCCESS; + if (wExtraKey.size()) + { + ok = ok && RegSetValueExW(k, wExtraKey.c_str(), 0, REG_SZ, reinterpret_cast(wExtraValue.c_str()), (wExtraValue.size() + 1) * 2) == ERROR_SUCCESS; + } + RegCloseKey(k); + return ok; + }; + + CoInitializeEx(NULL, COINIT_MULTITHREADED); + auto exe = Platform::ExecutableName(); +#ifndef IDI_DOC_ICON + // make this fail so I don't remove #include "resource.h" again and get away with it +# error where muh IDI_DOC_ICON D: +#endif + auto icon = ByteString::Build(exe, ",-", IDI_DOC_ICON); + auto path = Platform::GetCwd(); + auto open = ByteString::Build("\"", exe, "\" ddir \"", path, "\" \"file://%1\""); + auto ptsave = ByteString::Build("\"", exe, "\" ddir \"", path, "\" \"%1\""); + deleteKey("Software\\Classes\\ptsave"); + deleteKey("Software\\Classes\\.cps"); + deleteKey("Software\\Classes\\.stm"); + deleteKey("Software\\Classes\\PowderToySave"); + ok = ok && createKey("Software\\Classes\\ptsave", "Powder Toy Save", "URL Protocol", ""); + ok = ok && createKey("Software\\Classes\\ptsave\\DefaultIcon", icon); + ok = ok && createKey("Software\\Classes\\ptsave\\shell\\open\\command", ptsave); + ok = ok && createKey("Software\\Classes\\.cps", "PowderToySave"); + ok = ok && createKey("Software\\Classes\\.stm", "PowderToySave"); + ok = ok && createKey("Software\\Classes\\PowderToySave", "Powder Toy Save"); + ok = ok && createKey("Software\\Classes\\PowderToySave\\DefaultIcon", icon); + ok = ok && createKey("Software\\Classes\\PowderToySave\\shell\\open\\command", open); + IShellLinkW *shellLink = NULL; + IPersistFile *shellLinkPersist = NULL; + wchar_t programsPath[MAX_PATH]; + ok = ok && SHGetFolderPathW(NULL, CSIDL_PROGRAMS, NULL, SHGFP_TYPE_CURRENT, programsPath) == S_OK; + ok = ok && CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLinkW, (LPVOID *)&shellLink) == S_OK; + ok = ok && shellLink->SetPath(Platform::WinWiden(exe).c_str()) == S_OK; + ok = ok && shellLink->SetWorkingDirectory(Platform::WinWiden(path).c_str()) == S_OK; + ok = ok && shellLink->SetDescription(Platform::WinWiden(APPNAME).c_str()) == S_OK; + ok = ok && shellLink->QueryInterface(IID_IPersistFile, (LPVOID *)&shellLinkPersist) == S_OK; + ok = ok && shellLinkPersist->Save(Platform::WinWiden(ByteString::Build(Platform::WinNarrow(programsPath), "\\", APPNAME, ".lnk")).c_str(), TRUE) == S_OK; + if (shellLinkPersist) + { + shellLinkPersist->Release(); + } + if (shellLink) + { + shellLink->Release(); + } + CoUninitialize(); + return ok; +} + +bool UpdateStart(const std::vector &data) +{ + ByteString exeName = Platform::ExecutableName(), updName; + + if (!exeName.length()) + return false; + + updName = exeName; + ByteString extension = exeName.substr(exeName.length() - 4); + if (extension == ".exe") + updName = exeName.substr(0, exeName.length() - 4); + updName = updName + "_upd.exe"; + + if (!RenameFile(exeName, updName, false)) + return false; + + if (!WriteFile(data, exeName)) + { + Platform::RemoveFile(exeName); + return false; + } + + if ((uintptr_t)ShellExecute(NULL, L"open", Platform::WinWiden(exeName).c_str(), NULL, NULL, SW_SHOWNORMAL) <= 32) + { + Platform::RemoveFile(exeName); + return false; + } + + return true; +} + +bool UpdateFinish() +{ + ByteString exeName = Platform::ExecutableName(), updName; + int timeout = 5, err; + if constexpr (DEBUG) + { + printf("Update: Current EXE name: %s\n", exeName.c_str()); + } + updName = exeName; + ByteString extension = exeName.substr(exeName.length() - 4); + if (extension == ".exe") + updName = exeName.substr(0, exeName.length() - 4); + updName = updName + "_upd.exe"; + if constexpr (DEBUG) + { + printf("Update: Temp EXE name: %s\n", updName.c_str()); + } + while (!Platform::RemoveFile(updName)) + { + err = GetLastError(); + if (err == ERROR_FILE_NOT_FOUND) + { + if constexpr (DEBUG) + { + printf("Update: Temp file not deleted\n"); + } + // Old versions of powder toy name their update files with _update.exe, delete that upgrade file here + updName = exeName; + ByteString extension = exeName.substr(exeName.length() - 4); + if (extension == ".exe") + updName = exeName.substr(0, exeName.length() - 4); + updName = updName + "_update.exe"; + Platform::RemoveFile(updName); + return true; + } + Sleep(500); + timeout--; + if (timeout <= 0) + { + if constexpr (DEBUG) + { + printf("Update: Delete timeout\n"); + } + return false; + } + } + return true; +} + +void UpdateCleanup() +{ + UpdateFinish(); +} + +void SetupCrt() +{ + _setmode(0, _O_BINARY); + _setmode(1, _O_BINARY); + SetConsoleCP(CP_UTF8); + SetConsoleOutputCP(CP_UTF8); + if constexpr (DEBUG) + { + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG); + } + if (GetACP() != CP_UTF8) + { + std::cerr << "failed to set codepage to utf-8, expect breakage" << std::endl; + } +} +} diff --git a/src/common/platform/meson.build b/src/common/platform/meson.build new file mode 100644 index 000000000..20d4706db --- /dev/null +++ b/src/common/platform/meson.build @@ -0,0 +1,56 @@ +common_files += files( + 'Common.cpp', +) + +can_install_enforce_no = false +set_window_icon = false +path_sep_char = '/' +if host_platform == 'windows' + path_sep_char = '\\\\' + common_files += files( + 'Windows.cpp', + ) +elif host_platform == 'darwin' + can_install_enforce_no = true + common_files += files( + 'Darwin.cpp', + 'Posix.cpp', + ) +elif host_platform == 'android' + can_install_enforce_no = true + common_files += files( + 'Android.cpp', + 'Posix.cpp', + ) +elif host_platform == 'linux' + # TODO: again, this is more like "posix" than "linux" + set_window_icon = true + common_files += files( + 'Linux.cpp', + 'Posix.cpp', + ) +else + can_install_enforce_no = true + common_files += files( + 'Null.cpp', + 'Posix.cpp', + ) +endif +conf_data.set('SET_WINDOW_ICON', set_window_icon ? 'true' : 'false') +conf_data.set('PATH_SEP_CHAR', path_sep_char) + +can_install = get_option('can_install') +if can_install == 'auto' + can_install = 'yes_check' + if is_debug + can_install = 'yes' + endif + if can_install_enforce_no + can_install = 'no' + endif +endif +if can_install != 'no' and can_install_enforce_no + error('cannot provide install support, configure with -Dcan_install=no to fix this error') +endif +conf_data.set('CAN_INSTALL', can_install != 'no' ? 'true' : 'false') +conf_data.set('INSTALL_CHECK', can_install == 'yes_check' ? 'true' : 'false') diff --git a/src/common/tpt-compat.h b/src/common/tpt-compat.h index fee84c545..52356c231 100644 --- a/src/common/tpt-compat.h +++ b/src/common/tpt-compat.h @@ -13,22 +13,21 @@ * along with this program. If not, see . */ -#ifndef TPT_COMPAT_H -#define TPT_COMPAT_H - - +#pragma once #include #include #include #include -//some compatibility stuff for non-standard compilers -#if defined(WIN) && !defined(strcasecmp) -#define strcasecmp stricmp -#endif +#define _USE_MATH_DEFINES +#include -#ifndef M_PI -#define M_PI 3.14159265f +#ifdef M_PI +constexpr float TPT_PI_FLT = float(M_PI); +constexpr double TPT_PI_DBL = double(M_PI); +#else +constexpr float TPT_PI_FLT = 3.14159265f; +constexpr double TPT_PI_DBL = 3.14159265358979323846; #endif typedef unsigned short Uint16; @@ -36,5 +35,3 @@ typedef unsigned short Uint16; #ifndef NULL # define NULL 0 #endif - -#endif diff --git a/src/common/tpt-inline.h b/src/common/tpt-inline.h index 304e762b0..6f0996e5c 100644 --- a/src/common/tpt-inline.h +++ b/src/common/tpt-inline.h @@ -13,13 +13,9 @@ * along with this program. If not, see . */ -#ifndef TPT_INLINE_H -#define TPT_INLINE_H - +#pragma once #if defined(_MSC_VER) -#define TPT_INLINE _inline +# define TPT_INLINE _inline #else -#define TPT_INLINE inline -#endif - +# define TPT_INLINE inline #endif diff --git a/src/common/tpt-minmax.h b/src/common/tpt-minmax.h index 729bd535f..a19a1d66a 100644 --- a/src/common/tpt-minmax.h +++ b/src/common/tpt-minmax.h @@ -13,9 +13,7 @@ * along with this program. If not, see . */ -#ifndef TPT_MINMAX_H -#define TPT_MINMAX_H - +#pragma once #include #ifdef _MSC_VER @@ -27,5 +25,3 @@ # undef max # endif #endif - -#endif diff --git a/src/common/tpt-rand.cpp b/src/common/tpt-rand.cpp index c93a88ae7..5a076d8c7 100644 --- a/src/common/tpt-rand.cpp +++ b/src/common/tpt-rand.cpp @@ -62,4 +62,4 @@ void RNG::seed(unsigned int sd) s[1] = sd; } -RNG random_gen; +RNG interfaceRng; diff --git a/src/common/tpt-rand.h b/src/common/tpt-rand.h index 045349a7a..9eb4ed29a 100644 --- a/src/common/tpt-rand.h +++ b/src/common/tpt-rand.h @@ -1,14 +1,15 @@ -#ifndef TPT_RAND_ -#define TPT_RAND_ -#include "Config.h" - +#pragma once +#include "ExplicitSingleton.h" #include -#include "Singleton.h" +#include -class RNG : public Singleton +class RNG { +public: + using State = std::array; + private: - uint64_t s[2]; + State s; uint64_t next(); public: unsigned int operator()(); @@ -19,8 +20,19 @@ public: RNG(); void seed(unsigned int sd); + + void state(State ns) + { + s = ns; + } + + State state() const + { + return s; + } }; -extern RNG random_gen; - -#endif /* TPT_RAND_ */ +// Please only use this on the main thread and never for simulation stuff. +// For simulation stuff, use Simulation::rng. For renderer stuff, use Renderer::rng. +// For anything else, prefer a dedicated RNG instance over this one. +extern RNG interfaceRng; diff --git a/src/common/tpt-thread-local.h b/src/common/tpt-thread-local.h index e6b6586f6..66f02eb75 100644 --- a/src/common/tpt-thread-local.h +++ b/src/common/tpt-thread-local.h @@ -1,6 +1,4 @@ #pragma once -#include "Config.h" - #ifdef __MINGW32__ # include diff --git a/src/config/font/meson.build b/src/config/font/meson.build deleted file mode 100644 index 44c65f26d..000000000 --- a/src/config/font/meson.build +++ /dev/null @@ -1,12 +0,0 @@ -font_conf_data = conf_data -font_conf_data.set('FONTEDITOR', true) -font_conf_data.set('RENDERER', false) -font_conf_data.set('LUACONSOLE', false) -font_conf_data.set('NOHTTP', true) -font_conf_data.set('GRAVFFT', false) -configure_file( - input: config_template, - output: 'Config.h', - configuration: font_conf_data -) -font_inc = include_directories('.') diff --git a/src/config/meson.build b/src/config/meson.build deleted file mode 100644 index b9d92080f..000000000 --- a/src/config/meson.build +++ /dev/null @@ -1,9 +0,0 @@ -if get_option('build_powder') - subdir('powder') -endif -if get_option('build_render') - subdir('render') -endif -if get_option('build_font') - subdir('font') -endif diff --git a/src/config/powder/meson.build b/src/config/powder/meson.build deleted file mode 100644 index 3fbc8cb55..000000000 --- a/src/config/powder/meson.build +++ /dev/null @@ -1,12 +0,0 @@ -powder_conf_data = conf_data -powder_conf_data.set('FONTEDITOR', false) -powder_conf_data.set('RENDERER', false) -powder_conf_data.set('LUACONSOLE', lua_variant != 'none') -powder_conf_data.set('NOHTTP', not enable_http) -powder_conf_data.set('GRAVFFT', enable_gravfft) -configure_file( - input: config_template, - output: 'Config.h', - configuration: powder_conf_data -) -powder_inc = include_directories('.') diff --git a/src/config/render/meson.build b/src/config/render/meson.build deleted file mode 100644 index aaa90b3be..000000000 --- a/src/config/render/meson.build +++ /dev/null @@ -1,12 +0,0 @@ -render_conf_data = conf_data -render_conf_data.set('FONTEDITOR', false) -render_conf_data.set('RENDERER', true) -render_conf_data.set('LUACONSOLE', false) -render_conf_data.set('NOHTTP', true) -render_conf_data.set('GRAVFFT', false) -configure_file( - input: config_template, - output: 'Config.h', - configuration: render_conf_data -) -render_inc = include_directories('.') diff --git a/src/debug/DebugInfo.h b/src/debug/DebugInfo.h index 4c2346341..43f10edb3 100644 --- a/src/debug/DebugInfo.h +++ b/src/debug/DebugInfo.h @@ -1,6 +1,4 @@ #pragma once -#include "Config.h" - #include "gui/interface/Point.h" class DebugInfo diff --git a/src/debug/DebugLines.cpp b/src/debug/DebugLines.cpp index 12e9521a3..74d119595 100644 --- a/src/debug/DebugLines.cpp +++ b/src/debug/DebugLines.cpp @@ -1,10 +1,8 @@ #include "DebugLines.h" - #include "gui/game/GameView.h" - #include "gui/game/GameController.h" - #include "graphics/Graphics.h" +#include "SimulationConfig.h" DebugLines::DebugLines(unsigned int id, GameView * view, GameController * controller): DebugInfo(id), @@ -23,26 +21,25 @@ void DebugLines::Draw() ui::Point drawPoint1 = controller->PointTranslate(view->GetLineStartCoords()), drawPoint2 = controller->PointTranslate(view->GetLineFinishCoords()); if (view->GetDrawSnap()) drawPoint2 = view->lineSnapCoords(drawPoint1, drawPoint2); - //g->draw_line(drawPoint1.X, drawPoint1.Y, drawPoint2.X, drawPoint2.Y, 255, 0, 255, 255); - g->draw_line(0, drawPoint1.Y, XRES, drawPoint1.Y, 255, 255, 255, 120); - g->draw_line(drawPoint1.X, 0, drawPoint1.X, YRES, 255, 255, 255, 120); + g->BlendLine({ 0, drawPoint1.Y }, { XRES, drawPoint1.Y }, 0xFFFFFF_rgb .WithAlpha(120)); + g->BlendLine({ drawPoint1.X, 0 }, { drawPoint1.X, YRES }, 0xFFFFFF_rgb .WithAlpha(120)); - g->draw_line(0, drawPoint2.Y, XRES, drawPoint2.Y, 255, 255, 255, 120); - g->draw_line(drawPoint2.X, 0, drawPoint2.X, YRES, 255, 255, 255, 120); + g->BlendLine({ 0, drawPoint2.Y }, { XRES, drawPoint2.Y }, 0xFFFFFF_rgb .WithAlpha(120)); + g->BlendLine({ drawPoint2.X, 0 }, { drawPoint2.X, YRES }, 0xFFFFFF_rgb .WithAlpha(120)); String info; info = String::Build(drawPoint2.X, " x ", drawPoint2.Y); - g->drawtext_outline(drawPoint2.X+(drawPoint2.X>drawPoint1.X?3:-g->textwidth(info)-3), drawPoint2.Y+(drawPoint2.YBlendTextOutline({ drawPoint2.X+(drawPoint2.X>drawPoint1.X?3:-(g->TextSize(info).X-1)-3), drawPoint2.Y+(drawPoint2.Ydrawtext_outline(drawPoint1.X+(drawPoint2.Xtextwidth(info)-2), drawPoint1.Y+(drawPoint2.Y>drawPoint1.Y?-10:3), info, 255, 255, 255, 200); + g->BlendTextOutline({ drawPoint1.X+(drawPoint2.XTextSize(info).X-1)-2), drawPoint1.Y+(drawPoint2.Y>drawPoint1.Y?-10:3) }, info, 0xFFFFFF_rgb .WithAlpha(200)); info = String::Build(std::abs(drawPoint2.X-drawPoint1.X)); - g->drawtext_outline((drawPoint1.X+drawPoint2.X)/2-g->textwidth(info)/2, drawPoint1.Y+(drawPoint2.Y>drawPoint1.Y?-10:3), info, 255, 255, 255, 200); + g->BlendTextOutline({ (drawPoint1.X+drawPoint2.X)/2-(g->TextSize(info).X-1)/2, drawPoint1.Y+(drawPoint2.Y>drawPoint1.Y?-10:3) }, info, 0xFFFFFF_rgb .WithAlpha(200)); info = String::Build(std::abs(drawPoint2.Y-drawPoint1.Y)); - g->drawtext_outline(drawPoint1.X+(drawPoint2.Xtextwidth(info)-2), (drawPoint1.Y+drawPoint2.Y)/2-3, info, 255, 255, 255, 200); + g->BlendTextOutline({ drawPoint1.X+(drawPoint2.XTextSize(info).X-1)-2), (drawPoint1.Y+drawPoint2.Y)/2-3 }, info, 0xFFFFFF_rgb .WithAlpha(200)); } } diff --git a/src/debug/DebugParts.cpp b/src/debug/DebugParts.cpp index 8587b8a33..30748cd5b 100644 --- a/src/debug/DebugParts.cpp +++ b/src/debug/DebugParts.cpp @@ -22,9 +22,9 @@ void DebugParts::Draw() for (int i = 0; i < NPART; i++) { if (sim->parts[i].type) - g->addpixel(x, y, 255, 255, 255, 180); + g->AddPixel({ x, y }, 0xFFFFFF_rgb .WithAlpha(180)); else - g->addpixel(x, y, 0, 0, 0, 180); + g->AddPixel({ x, y }, 0x000000_rgb .WithAlpha(180)); if (i == sim->parts_lastActiveIndex) { @@ -38,17 +38,17 @@ void DebugParts::Draw() x = 0; } } - g->draw_line(0, lpy, XRES, lpy, 0, 255, 120, 255); - g->draw_line(lpx, 0, lpx, YRES, 0, 255, 120, 255); - g->addpixel(lpx, lpy, 255, 50, 50, 220); + g->DrawLine({ 0, lpy }, { XRES, lpy }, 0x00FF78_rgb); + g->DrawLine({ lpx, 0 }, { lpx, YRES }, 0x00FF78_rgb); + g->AddPixel({ lpx, lpy }, 0xFF3232_rgb .WithAlpha(220)); - g->addpixel(lpx+1, lpy, 255, 50, 50, 120); - g->addpixel(lpx-1, lpy, 255, 50, 50, 120); - g->addpixel(lpx, lpy+1, 255, 50, 50, 120); - g->addpixel(lpx, lpy-1, 255, 50, 50, 120); + g->AddPixel({ lpx+1, lpy }, 0xFF3232_rgb .WithAlpha(120)); + g->AddPixel({ lpx-1, lpy }, 0xFF3232_rgb .WithAlpha(120)); + g->AddPixel({ lpx, lpy+1 }, 0xFF3232_rgb .WithAlpha(120)); + g->AddPixel({ lpx, lpy-1 }, 0xFF3232_rgb .WithAlpha(120)); - g->fillrect(7, YRES-26, g->textwidth(info)+5, 14, 0, 0, 0, 180); - g->drawtext(10, YRES-22, info, 255, 255, 255, 255); + g->BlendFilledRect(RectSized(Vec2{ 7, YRES-26}, Vec2{ g->TextSize(info).X + 4, 14}), 0x000000_rgb .WithAlpha(180)); + g->BlendText({ 10, YRES-22 }, info, 0xFFFFFF_rgb .WithAlpha(255)); } DebugParts::~DebugParts() diff --git a/src/debug/ElementPopulation.cpp b/src/debug/ElementPopulation.cpp index 9833b544e..76ec04133 100644 --- a/src/debug/ElementPopulation.cpp +++ b/src/debug/ElementPopulation.cpp @@ -44,7 +44,7 @@ void ElementPopulationDebug::Draw() halfValString = String::Build(maxAverage/2); - g->fillrect(xStart-5, yBottom - 263, bars+10+Graphics::textwidth(maxValString)+10, 255 + 13, 0, 0, 0, 180); + g->BlendFilledRect(RectSized(Vec2{ xStart-5, yBottom - 263 }, Vec2{ bars+10+Graphics::TextSize(maxValString).X+9, 255 + 13 }), 0x000000_rgb .WithAlpha(180)); bars = 0; for(int i = 0; i < PT_NUM; i++) @@ -55,28 +55,30 @@ void ElementPopulationDebug::Draw() auto barSize = int(count * scale - 0.5f); int barX = bars;//*2; - g->draw_line(xStart+barX, yBottom+3, xStart+barX, yBottom+2, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), 255); + RGB colour = sim->elements[i].Colour; + + g->DrawLine({ xStart+barX, yBottom+3 }, { xStart+barX, yBottom+2 }, colour); if(sim->elementCount[i]) { if(barSize > 256) { barSize = 256; - g->blendpixel(xStart+barX, yBottom-barSize-3, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), 255); - g->blendpixel(xStart+barX, yBottom-barSize-5, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), 255); - g->blendpixel(xStart+barX, yBottom-barSize-7, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), 255); + g->DrawPixel({ xStart+barX, yBottom-barSize-3 }, colour); + g->DrawPixel({ xStart+barX, yBottom-barSize-5 }, colour); + g->DrawPixel({ xStart+barX, yBottom-barSize-7 }, colour); } else { - g->draw_line(xStart+barX, yBottom-barSize-3, xStart+barX, yBottom-barSize-2, 255, 255, 255, 180); + g->BlendLine({ xStart+barX, yBottom-barSize-3 }, { xStart+barX, yBottom-barSize-2 }, 0xFFFFFF_rgb .WithAlpha(180)); } - g->draw_line(xStart+barX, yBottom-barSize, xStart+barX, yBottom, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), 255); + g->DrawLine({ xStart+barX, yBottom-barSize }, { xStart+barX, yBottom }, colour); } bars++; } } - g->drawtext(xStart + bars + 5, yBottom-5, "0", 255, 255, 255, 255); - g->drawtext(xStart + bars + 5, yBottom-132, halfValString, 255, 255, 255, 255); - g->drawtext(xStart + bars + 5, yBottom-260, maxValString, 255, 255, 255, 255); + g->BlendText({ xStart + bars + 5, yBottom-5 }, "0", 0xFFFFFF_rgb .WithAlpha(255)); + g->BlendText({ xStart + bars + 5, yBottom-132 }, halfValString, 0xFFFFFF_rgb .WithAlpha(255)); + g->BlendText({ xStart + bars + 5, yBottom-260 }, maxValString, 0xFFFFFF_rgb .WithAlpha(255)); } ElementPopulationDebug::~ElementPopulationDebug() diff --git a/src/debug/ParticleDebug.cpp b/src/debug/ParticleDebug.cpp index 2290459b1..aefe4a8b0 100644 --- a/src/debug/ParticleDebug.cpp +++ b/src/debug/ParticleDebug.cpp @@ -14,7 +14,6 @@ ParticleDebug::ParticleDebug(unsigned int id, Simulation * sim, GameModel * mode void ParticleDebug::Debug(int mode, int x, int y) { - int debug_currentParticle = sim->debug_currentParticle; int i = 0; String logmessage; @@ -22,7 +21,7 @@ void ParticleDebug::Debug(int mode, int x, int y) { if (!sim->NUM_PARTS) return; - i = debug_currentParticle; + i = sim->debug_nextToUpdate; while (i < NPART - 1 && !sim->parts[i].type) i++; if (i == NPART - 1) @@ -32,30 +31,31 @@ void ParticleDebug::Debug(int mode, int x, int y) } else if (mode == 1) { - if (x < 0 || x >= XRES || y < 0 || y >= YRES || !sim->pmap[y][x] || (i = ID(sim->pmap[y][x])) < debug_currentParticle) + i = NPART - 1; + if (x >= 0 && x < XRES && y >= 0 && y < YRES) { - i = NPART - 1; - logmessage = String::Build("Updated particles from #", debug_currentParticle, " to end, updated sim"); + if (sim->pmap[y][x] && ID(sim->pmap[y][x]) >= sim->debug_nextToUpdate) + { + i = ID(sim->pmap[y][x]); + } + else if (sim->photons[y][x] && ID(sim->photons[y][x]) >= sim->debug_nextToUpdate) + { + i = ID(sim->photons[y][x]); + } } - else - logmessage = String::Build("Updated particles #", debug_currentParticle, " through #", i); } - model->Log(logmessage, false); - - if (sim->debug_currentParticle == 0) + sim->framerender = 1; + auto prevToUpdate = sim->debug_nextToUpdate; + model->UpdateUpTo(i + 1); + if (sim->debug_nextToUpdate) { - sim->framerender = 1; - sim->BeforeSim(); - sim->framerender = 0; + logmessage = String::Build("Updated particles from #", prevToUpdate, " through #", i); } - sim->UpdateParticles(debug_currentParticle, i); - if (i < NPART-1) - sim->debug_currentParticle = i+1; else { - sim->AfterSim(); - sim->debug_currentParticle = 0; + logmessage = String::Build("Updated particles from #", prevToUpdate, " to end"); } + model->Log(logmessage, false); } bool ParticleDebug::KeyPress(int key, int scan, bool shift, bool ctrl, bool alt, ui::Point currentMouse) @@ -86,13 +86,11 @@ bool ParticleDebug::KeyPress(int key, int scan, bool shift, bool ctrl, bool alt, { if (ctrl) return true; - if (sim->debug_currentParticle > 0) + if (sim->debug_nextToUpdate > 0) { - sim->UpdateParticles(sim->debug_currentParticle, NPART - 1); - sim->AfterSim(); - String logmessage = String::Build("Updated particles from #", sim->debug_currentParticle, " to end, updated sim"); + String logmessage = String::Build("Updated particles from #", sim->debug_nextToUpdate, " to end due to frame step"); + model->UpdateUpTo(NPART); model->Log(logmessage, false); - sim->debug_currentParticle = 0; } else { @@ -103,8 +101,3 @@ bool ParticleDebug::KeyPress(int key, int scan, bool shift, bool ctrl, bool alt, } return true; } - -ParticleDebug::~ParticleDebug() -{ - -} diff --git a/src/debug/ParticleDebug.h b/src/debug/ParticleDebug.h index c6a3d3f36..dc134997d 100644 --- a/src/debug/ParticleDebug.h +++ b/src/debug/ParticleDebug.h @@ -1,6 +1,4 @@ -#ifndef PARTICLE_DEBUG_H -#define PARTICLE_DEBUG_H - +#pragma once #include "DebugInfo.h" class Simulation; @@ -13,7 +11,4 @@ public: ParticleDebug(unsigned int id, Simulation * sim, GameModel * model); void Debug(int mode, int x, int y); bool KeyPress(int key, int scan, bool shift, bool ctrl, bool alt, ui::Point currentMouse) override; - virtual ~ParticleDebug(); }; - -#endif diff --git a/src/graphics/DrawMethodsDef.inc b/src/graphics/DrawMethodsDef.inc deleted file mode 100644 index 663f4bbe0..000000000 --- a/src/graphics/DrawMethodsDef.inc +++ /dev/null @@ -1,16 +0,0 @@ - int drawtext(int x, int y, String s, int r, int g, int b, int a); - int drawchar(int x, int y, String::value_type c, int r, int g, int b, int a); - int addchar(int x, int y, String::value_type c, int r, int g, int b, int a); - - void xor_pixel(int x, int y); - void xor_line(int x, int y, int x2, int y2); - void xor_rect(int x, int y, int width, int height); - void xor_bitmap(unsigned char * bitmap, int x, int y, int w, int h); - - void draw_line(int x, int y, int x2, int y2, int r, int g, int b, int a); - void drawrect(int x, int y, int width, int height, int r, int g, int b, int a); - void fillrect(int x, int y, int width, int height, int r, int g, int b, int a); - void clearrect(int x, int y, int width, int height); - void gradientrect(int x, int y, int width, int height, int r, int g, int b, int a, int r2, int g2, int b2, int a2); - - void draw_image(pixel *img, int x, int y, int w, int h, int a); diff --git a/src/graphics/FontReader.cpp b/src/graphics/FontReader.cpp index ff09eb751..4141a11cc 100644 --- a/src/graphics/FontReader.cpp +++ b/src/graphics/FontReader.cpp @@ -4,6 +4,7 @@ #include "font.bz2.h" #include +#include unsigned char *font_data = nullptr; unsigned int *font_ptrs = nullptr; diff --git a/src/graphics/FontReader.h b/src/graphics/FontReader.h index 2bea64b7d..4aab75ab8 100644 --- a/src/graphics/FontReader.h +++ b/src/graphics/FontReader.h @@ -3,7 +3,7 @@ #include "common/String.h" -#define FONT_H 12 +constexpr auto FONT_H = 12; class FontReader { diff --git a/src/graphics/Graphics.cpp b/src/graphics/Graphics.cpp index e34e5a9f6..47165091b 100644 --- a/src/graphics/Graphics.cpp +++ b/src/graphics/Graphics.cpp @@ -1,453 +1,174 @@ -#include "Graphics.h" - +#include #include -#include #include #include - -#include -#include "common/Platform.h" - -#include "FontReader.h" -#ifdef HIGH_QUALITY_RESAMPLE -#include "resampler/resampler.h" -#endif - +#include +#include #include +#include "common/platform/Platform.h" +#include "FontReader.h" +#include "Format.h" +#include "Graphics.h" +#include "resampler/resampler.h" +#include "SimulationConfig.h" +#include "RasterDrawMethodsImpl.h" -VideoBuffer::VideoBuffer(int width, int height): - Width(width), - Height(height) -{ - Buffer = new pixel[width*height]; - std::fill(Buffer, Buffer+(width*height), 0); -}; +VideoBuffer::VideoBuffer(Vec2 size): + video(size) +{} -VideoBuffer::VideoBuffer(const VideoBuffer & old): - Width(old.Width), - Height(old.Height) +VideoBuffer::VideoBuffer(pixel const *data, Vec2 size): + VideoBuffer(size) { - Buffer = new pixel[old.Width*old.Height]; - std::copy(old.Buffer, old.Buffer+(old.Width*old.Height), Buffer); -}; - -VideoBuffer::VideoBuffer(VideoBuffer * old): - Width(old->Width), - Height(old->Height) -{ - Buffer = new pixel[old->Width*old->Height]; - std::copy(old->Buffer, old->Buffer+(old->Width*old->Height), Buffer); -}; - -VideoBuffer::VideoBuffer(pixel * buffer, int width, int height, int pitch): - Width(width), - Height(height) -{ - Buffer = new pixel[width*height]; - CopyData(buffer, width, height, pitch ? pitch : width); + std::copy_n(data, size.X * size.Y, video.data()); } -void VideoBuffer::CopyData(pixel * buffer, int width, int height, int pitch) +VideoBuffer::VideoBuffer(pixel const *data, Vec2 size, size_t rowStride): + VideoBuffer(size) { - for (auto y = 0; y < height; ++y) + for(int y = 0; y < size.Y; y++) + std::copy_n(data + rowStride * y, size.X, video.RowIterator(Vec2(0, y))); +} + +void VideoBuffer::Crop(Rect rect) +{ + rect &= Size().OriginRect(); + if (rect == Size().OriginRect()) + return; + + PlaneAdapter &> newVideo(rect.Size(), std::in_place, video.Base); + for (auto y = 0; y < newVideo.Size().Y; y++) + std::copy_n( + video.RowIterator(rect.TopLeft + Vec2(0, y)), + newVideo.Size().X, + newVideo.RowIterator(Vec2(0, y)) + ); + video.Base.resize(newVideo.Size().X * newVideo.Size().Y); + video.Base.shrink_to_fit(); + video.SetSize(newVideo.Size()); +} + +void VideoBuffer::Resize(Vec2 size, bool resample) +{ + if (size == Size()) + return; + + if (resample) { - std::copy(buffer + y * pitch, buffer + y * pitch + width, Buffer + y * width); - } -} + std::array, PIXELCHANNELS> resamplers; + Resampler::Contrib_List *clist_x = NULL, *clist_y = NULL; + for (auto &ptr : resamplers) + { + ptr = std::make_unique( + Size().X, Size().Y, // source size + size.X, size.Y, // destination size + Resampler::BOUNDARY_CLAMP, + 0.0f, 255.0f, // upper and lower bounds for channel values + "lanczos12", + clist_x, clist_y, + 0.75f, 0.75f // X and Y filter scales, values < 1.0 cause aliasing, but create sharper looking mips. + ); + clist_x = ptr->get_clist_x(); + clist_y = ptr->get_clist_y(); + } -void VideoBuffer::Crop(int width, int height, int x, int y) -{ - CopyData(Buffer + y * Width + x, width, height, Width); - Width = width; - Height = height; + std::array, PIXELCHANNELS> samples; + for (auto &ptr : samples) + ptr = std::make_unique(Size().X); + + PlaneAdapter> newVideo(size); + + pixel const *inIter = video.data(); + std::array outIter; + for (pixel *&it : outIter) + it = newVideo.data(); + + for (int sourceY = 0; sourceY < Size().Y; sourceY++) + { + for (int sourceX = 0; sourceX < Size().X; sourceX++) + { + pixel px = *inIter++; + for (int c = 0; c < PIXELCHANNELS; c++) + samples[c][sourceX] = uint8_t(px >> (8 * c)); + } + + for (int c = 0; c < PIXELCHANNELS; c++) + { + if (!resamplers[c]->put_line(samples[c].get())) + { + fprintf(stderr, "Out of memory when resampling\n"); + Crop(size.OriginRect()); // Better than leaving the image at original size I guess + return; + } + + while (float const *output = resamplers[c]->get_line()) + for (int destX = 0; destX < size.X; destX++) + *outIter[c]++ |= pixel(uint8_t(output[destX])) << (8 * c); + } + } + + video = std::move(newVideo); + } + else + { + PlaneAdapter> newVideo(size); + for (auto pos : size.OriginRect()) + { + auto oldPos = Vec2(pos.X * Size().X / size.X, pos.Y * Size().Y / size.Y); + newVideo[pos] = video[oldPos]; + } + video = std::move(newVideo); + } } void VideoBuffer::Resize(float factor, bool resample) { - int newWidth = int(Width * factor); - int newHeight = int(Height * factor); - Resize(newWidth, newHeight, resample); + Resize(Vec2{ int(Size().X * factor), int(Size().Y * factor) }, resample); } -void VideoBuffer::Resize(int width, int height, bool resample, bool fixedRatio) +void VideoBuffer::ResizeToFit(Vec2 bound, bool resample) { - int newWidth = width; - int newHeight = height; - pixel * newBuffer; - if(newHeight == -1 && newWidth == -1) - return; - if(newHeight == -1 || newWidth == -1) + Vec2 size = Size(); + if (size.X > bound.X || size.Y > bound.Y) { - if(newHeight == -1) - newHeight = int(float(Height) * newWidth / Width); - if(newWidth == -1) - newWidth = int(float(Width) * newHeight / Height); - } - else if(fixedRatio) - { - //Force proportions - if(newWidth*Height > newHeight*Width) // same as nW/W > nH/H - newWidth = (int)(Width * (newHeight/(float)Height)); + auto ceilDiv = [](int a, int b) { + return a / b + ((a % b) ? 1 : 0); + }; + if (bound.X * size.Y < bound.Y * size.X) + size = { bound.X, ceilDiv(size.Y * bound.X, size.X) }; else - newHeight = (int)(Height * (newWidth/(float)Width)); + size = { ceilDiv(size.X * bound.Y, size.Y), bound.Y }; + } + Resize(size, resample); +} + +std::unique_ptr VideoBuffer::FromPNG(std::vector const &data) +{ + auto video = format::PixelsFromPNG(data, 0x000000_rgb); + if (video) + { + auto buf = std::make_unique(Vec2::Zero); + buf->video = std::move(*video); + return buf; } - if(resample) - newBuffer = Graphics::resample_img(Buffer, Width, Height, newWidth, newHeight); else - newBuffer = Graphics::resample_img_nn(Buffer, Width, Height, newWidth, newHeight); - - if(newBuffer) - { - delete[] Buffer; - Buffer = newBuffer; - Width = newWidth; - Height = newHeight; - } + return nullptr; } -int VideoBuffer::SetCharacter(int x, int y, String::value_type c, int r, int g, int b, int a) +std::unique_ptr> VideoBuffer::ToPNG() const { - FontReader reader(c); - for (int j = -2; j < FONT_H - 2; j++) - for (int i = 0; i < reader.GetWidth(); i++) - SetPixel(x + i, y + j, r, g, b, reader.NextPixel() * a / 3); - return x + reader.GetWidth(); + return format::PixelsToPNG(video); } -int VideoBuffer::BlendCharacter(int x, int y, String::value_type c, int r, int g, int b, int a) +std::vector VideoBuffer::ToPPM() const { - FontReader reader(c); - for (int j = -2; j < FONT_H - 2; j++) - for (int i = 0; i < reader.GetWidth(); i++) - BlendPixel(x + i, y + j, r, g, b, reader.NextPixel() * a / 3); - return x + reader.GetWidth(); + return format::PixelsToPPM(video); } -int VideoBuffer::AddCharacter(int x, int y, String::value_type c, int r, int g, int b, int a) -{ - FontReader reader(c); - for (int j = -2; j < FONT_H - 2; j++) - for (int i = 0; i < reader.GetWidth(); i++) - AddPixel(x + i, y + j, r, g, b, reader.NextPixel() * a / 3); - return x + reader.GetWidth(); -} +template struct RasterDrawMethods; -VideoBuffer::~VideoBuffer() -{ - delete[] Buffer; -} - -pixel *Graphics::resample_img_nn(pixel * src, int sw, int sh, int rw, int rh) -{ - int y, x; - pixel *q = NULL; - q = new pixel[rw*rh]; - for (y=0; yget_clist_x(), resamplers[0]->get_clist_y(), filter_scale, filter_scale); - samples[i] = new float[sourceWidth]; - } - - unsigned char * resultImage = new unsigned char[resultHeight * resultPitch]; - std::fill(resultImage, resultImage + (resultHeight*resultPitch), 0); - - //Resample time - int resultY = 0; - for (int sourceY = 0; sourceY < sourceHeight; sourceY++) - { - unsigned char * sourcePixel = &source[sourceY * sourcePitch]; - - //Move pixel components into channel samples - for (int c = 0; c < PIXELCHANNELS; c++) - { - for (int x = 0; x < sourceWidth; x++) - { - samples[c][x] = sourcePixel[(x*PIXELSIZE)+c] * (1.0f/255.0f); - } - } - - //Put channel sample data into resampler - for (int c = 0; c < PIXELCHANNELS; c++) - { - if (!resamplers[c]->put_line(&samples[c][0])) - { - printf("Out of memory!\n"); - return NULL; - } - } - - //Perform resample and Copy components from resampler result samples to image buffer - for ( ; ; ) - { - int comp_index; - for (comp_index = 0; comp_index < PIXELCHANNELS; comp_index++) - { - const float* resultSamples = resamplers[comp_index]->get_line(); - if (!resultSamples) - break; - - unsigned char * resultPixel = &resultImage[(resultY * resultPitch) + comp_index]; - - for (int x = 0; x < resultWidth; x++) - { - int c = (int)(255.0f * resultSamples[x] + .5f); - if (c < 0) c = 0; else if (c > 255) c = 255; - *resultPixel = (unsigned char)c; - resultPixel += PIXELSIZE; - } - } - if (comp_index < PIXELCHANNELS) - break; - - resultY++; - } - } - - //Clean up - for(int i = 0; i < PIXELCHANNELS; i++) - { - delete resamplers[i]; - delete[] samples[i]; - } - - return (pixel*)resultImage; -#else -#ifdef DEBUG - std::cout << "Resampling " << sw << "x" << sh << " to " << rw << "x" << rh << std::endl; -#endif - bool stairstep = false; - if(rw < sw || rh < sh) - { - float fx = (float)(((float)sw)/((float)rw)); - float fy = (float)(((float)sh)/((float)rh)); - - int fxint, fyint; - double fxintp_t, fyintp_t; - - float fxf = modf(fx, &fxintp_t), fyf = modf(fy, &fyintp_t); - fxint = fxintp_t; - fyint = fyintp_t; - - if(((fxint & (fxint-1)) == 0 && fxf < 0.1f) || ((fyint & (fyint-1)) == 0 && fyf < 0.1f)) - stairstep = true; - -#ifdef DEBUG - if(stairstep) - std::cout << "Downsampling by " << fx << "x" << fy << " using stairstepping" << std::endl; - else - std::cout << "Downsampling by " << fx << "x" << fy << " without stairstepping" << std::endl; -#endif - } - - int y, x, fxceil, fyceil; - //int i,j,x,y,w,h,r,g,b,c; - pixel *q = NULL; - if(rw == sw && rh == sh){ - //Don't resample - q = new pixel[rw*rh]; - std::copy(src, src+(rw*rh), q); - } else if(!stairstep) { - float fx, fy, fyc, fxc; - double intp; - pixel tr, tl, br, bl; - q = new pixel[rw*rh]; - //Bilinear interpolation for upscaling - for (y=0; y=sw) fxceil = sw-1; - if (fyceil>=sh) fyceil = sh-1; - tr = src[sw*(int)floor(fy)+fxceil]; - tl = src[sw*(int)floor(fy)+(int)floor(fx)]; - br = src[sw*fyceil+fxceil]; - bl = src[sw*fyceil+(int)floor(fx)]; - q[rw*y+x] = PIXRGB( - (int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)), - (int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)), - (int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc)) - ); - } - } else { - //Stairstepping - float fx, fy, fyc, fxc; - double intp; - pixel tr, tl, br, bl; - int rrw = rw, rrh = rh; - pixel * oq; - oq = new pixel[sw*sh]; - std::copy(src, src+(sw*sh), oq); - rw = sw; - rh = sh; - while(rrw != rw && rrh != rh){ - if(rw > rrw) - rw *= 0.7; - if(rh > rrh) - rh *= 0.7; - if(rw <= rrw) - rw = rrw; - if(rh <= rrh) - rh = rrh; - q = new pixel[rw*rh]; - //Bilinear interpolation - for (y=0; y=sw) fxceil = sw-1; - if (fyceil>=sh) fyceil = sh-1; - tr = oq[sw*(int)floor(fy)+fxceil]; - tl = oq[sw*(int)floor(fy)+(int)floor(fx)]; - br = oq[sw*fyceil+fxceil]; - bl = oq[sw*fyceil+(int)floor(fx)]; - q[rw*y+x] = PIXRGB( - (int)(((((float)PIXR(tl))*(1.0f-fxc))+(((float)PIXR(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXR(bl))*(1.0f-fxc))+(((float)PIXR(br))*(fxc)))*(fyc)), - (int)(((((float)PIXG(tl))*(1.0f-fxc))+(((float)PIXG(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXG(bl))*(1.0f-fxc))+(((float)PIXG(br))*(fxc)))*(fyc)), - (int)(((((float)PIXB(tl))*(1.0f-fxc))+(((float)PIXB(tr))*(fxc)))*(1.0f-fyc) + ((((float)PIXB(bl))*(1.0f-fxc))+(((float)PIXB(br))*(fxc)))*(fyc)) - ); - } - delete[] oq; - oq = q; - sw = rw; - sh = rh; - } - } - return q; -#endif -} - -int Graphics::textwidth(const String &str) -{ - int x = 0; - for (size_t i = 0; i < str.length(); i++) - { - if (str[i] == '\b') - { - if (str.length() <= i+1) - break; - i++; - continue; - } - else if (str[i] == '\x0F') - { - if (str.length() <= i+3) - break; - i += 3; - continue; - } - x += FontReader(str[i]).GetWidth(); - } - return x-1; -} - -int Graphics::CharWidth(String::value_type c) -{ - return FontReader(c).GetWidth(); -} - -int Graphics::textwidthx(const String &str, int w) -{ - int x = 0,n = 0,cw = 0; - for (size_t i = 0; i < str.length(); i++) - { - if (str[i] == '\b') - { - if (str.length() <= i+1) - break; - i++; - continue; - } else if (str[i] == '\x0F') { - if (str.length() <= i+3) - break; - i += 3; - continue; - } - cw = FontReader(str[i]).GetWidth(); - if (x+(cw/2) >= w) - break; - x += cw; - n++; - } - return n; -} - -void Graphics::textsize(const String &str, int & width, int & height) -{ - if(!str.size()) - { - width = 0; - height = FONT_H-2; - return; - } - - int cHeight = FONT_H-2, cWidth = 0, lWidth = 0; - for (size_t i = 0; i < str.length(); i++) - { - if (str[i] == '\n') - { - cWidth = 0; - cHeight += FONT_H; - } - else if (str[i] == '\x0F') - { - if (str.length() <= i+3) - break; - i += 3; - } - else if (str[i] == '\b') - { - if (str.length() <= i+1) - break; - i++; - } - else - { - cWidth += FontReader(str[i]).GetWidth(); - if(cWidth>lWidth) - lWidth = cWidth; - } - } - width = lWidth; - height = cHeight; -} +Graphics::Graphics() +{} void Graphics::draw_icon(int x, int y, Icon icon, unsigned char alpha, bool invert) { @@ -456,462 +177,284 @@ void Graphics::draw_icon(int x, int y, Icon icon, unsigned char alpha, bool inve { case IconOpen: if(invert) - drawchar(x, y, 0xE001, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE001, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE001, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE001, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconReload: if(invert) - drawchar(x, y, 0xE011, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE011, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE011, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE011, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconSave: if(invert) - drawchar(x, y, 0xE002, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE002, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE002, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE002, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconVoteUp: if(invert) { - drawchar(x-11, y+1, 0xE04B, 0, 100, 0, alpha); - drawtext(x+2, y+1, "Vote", 0, 100, 0, alpha); + BlendChar({ x-11, y+1 }, 0xE04B, 0x006400_rgb .WithAlpha(alpha)); + BlendText({ x+2, y+1 }, "Vote", 0x006400_rgb .WithAlpha(alpha)); } else { - drawchar(x-11, y+1, 0xE04B, 0, 187, 18, alpha); - drawtext(x+2, y+1, "Vote", 0, 187, 18, alpha); + BlendChar({ x-11, y+1 }, 0xE04B, 0x00BB12_rgb .WithAlpha(alpha)); + BlendText({ x+2, y+1 }, "Vote", 0x00BB12_rgb .WithAlpha(alpha)); } break; case IconVoteDown: if(invert) - drawchar(x, y, 0xE04A, 100, 10, 0, alpha); + BlendChar({ x, y }, 0xE04A, 0x640A00_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE04A, 187, 40, 0, alpha); + BlendChar({ x, y }, 0xE04A, 0xBB2800_rgb .WithAlpha(alpha)); break; case IconTag: if(invert) - drawchar(x, y, 0xE003, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE003, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE003, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE003, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconNew: if(invert) - drawchar(x, y, 0xE012, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE012, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE012, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE012, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconLogin: if(invert) - drawchar(x, y+1, 0xE004, 0, 0, 0, alpha); + BlendChar({ x, y + 1 }, 0xE004, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y+1, 0xE004, 255, 255, 255, alpha); + BlendChar({ x, y + 1 }, 0xE004, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconSimulationSettings: if(invert) - drawchar(x, y+1, 0xE04F, 0, 0, 0, alpha); + BlendChar({ x, y + 1 }, 0xE04F, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y+1, 0xE04F, 255, 255, 255, alpha); + BlendChar({ x, y + 1 }, 0xE04F, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconRenderSettings: if(invert) { - drawchar(x, y+1, 0xE058, 255, 0, 0, alpha); - drawchar(x, y+1, 0xE059, 0, 255, 0, alpha); - drawchar(x, y+1, 0xE05A, 0, 0, 255, alpha); + BlendChar({ x, y + 1 }, 0xE058, 0xFF0000_rgb .WithAlpha(alpha)); + BlendChar({ x, y + 1 }, 0xE059, 0x00FF00_rgb .WithAlpha(alpha)); + BlendChar({ x, y + 1 }, 0xE05A, 0x0000FF_rgb .WithAlpha(alpha)); } else { - addchar(x, y+1, 0xE058, 255, 0, 0, alpha); - addchar(x, y+1, 0xE059, 0, 255, 0, alpha); - addchar(x, y+1, 0xE05A, 0, 0, 255, alpha); + AddChar({ x, y + 1 }, 0xE058, 0xFF0000_rgb .WithAlpha(alpha)); + AddChar({ x, y + 1 }, 0xE059, 0x00FF00_rgb .WithAlpha(alpha)); + AddChar({ x, y + 1 }, 0xE05A, 0x0000FF_rgb .WithAlpha(alpha)); } break; case IconPause: if(invert) - drawchar(x, y, 0xE010, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE010, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE010, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE010, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconFavourite: if(invert) - drawchar(x, y, 0xE04C, 100, 80, 32, alpha); + BlendChar({ x, y }, 0xE04C, 0x645020_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE04C, 192, 160, 64, alpha); + BlendChar({ x, y }, 0xE04C, 0xC0A040_rgb .WithAlpha(alpha)); break; case IconReport: if(invert) - drawchar(x, y, 0xE063, 140, 140, 0, alpha); + BlendChar({ x, y }, 0xE063, 0x8C8C00_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE063, 255, 255, 0, alpha); + BlendChar({ x, y }, 0xE063, 0xFFFF00_rgb .WithAlpha(alpha)); break; case IconUsername: if(invert) { - drawchar(x, y, 0xE00B, 32, 64, 128, alpha); - drawchar(x, y, 0xE00A, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE00B, 0x204080_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE00A, 0x000000_rgb .WithAlpha(alpha)); } else { - drawchar(x, y, 0xE00B, 32, 64, 128, alpha); - drawchar(x, y, 0xE00A, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE00B, 0x204080_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE00A, 0xFFFFFF_rgb .WithAlpha(alpha)); } break; case IconPassword: if(invert) { - drawchar(x, y, 0xE00C, 160, 144, 32, alpha); - drawchar(x, y, 0xE004, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE00C, 0xA09020_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE004, 0x000000_rgb .WithAlpha(alpha)); } else { - drawchar(x, y, 0xE00C, 160, 144, 32, alpha); - drawchar(x, y, 0xE004, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE00C, 0xA09020_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE004, 0xFFFFFF_rgb .WithAlpha(alpha)); } break; case IconClose: if(invert) - drawchar(x, y, 0xE02A, 20, 20, 20, alpha); + BlendChar({ x, y }, 0xE02A, 0x141414_rgb .WithAlpha(alpha)); else - drawchar(x, y, 0xE02A, 230, 230, 230, alpha); + BlendChar({ x, y }, 0xE02A, 0xE6E6E6_rgb .WithAlpha(alpha)); break; case IconVoteSort: if (invert) { - drawchar(x, y, 0xE029, 44, 48, 32, alpha); - drawchar(x, y, 0xE028, 32, 44, 32, alpha); - drawchar(x, y, 0xE027, 128, 128, 128, alpha); + BlendChar({ x, y }, 0xE029, 0x2C3020_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE028, 0x202C20_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE027, 0x808080_rgb .WithAlpha(alpha)); } else { - drawchar(x, y, 0xE029, 144, 48, 32, alpha); - drawchar(x, y, 0xE028, 32, 144, 32, alpha); - drawchar(x, y, 0xE027, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE029, 0x903020_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE028, 0x209020_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE027, 0xFFFFFF_rgb .WithAlpha(alpha)); } break; case IconDateSort: if (invert) { - drawchar(x, y, 0xE026, 32, 32, 32, alpha); + BlendChar({ x, y }, 0xE026, 0x202020_rgb .WithAlpha(alpha)); } else { - drawchar(x, y, 0xE026, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE026, 0xFFFFFF_rgb .WithAlpha(alpha)); } break; case IconMyOwn: if (invert) { - drawchar(x, y, 0xE014, 192, 160, 64, alpha); - drawchar(x, y, 0xE013, 32, 32, 32, alpha); + BlendChar({ x, y }, 0xE014, 0xC0A040_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE013, 0x202020_rgb .WithAlpha(alpha)); } else { - drawchar(x, y, 0xE014, 192, 160, 64, alpha); - drawchar(x, y, 0xE013, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE014, 0xC0A040_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE013, 0xFFFFFF_rgb .WithAlpha(alpha)); } break; case IconSearch: - drawchar(x, y, 0xE00E, 30, 30, 180, alpha); - drawchar(x, y, 0xE00F, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE00E, 0x1E1EB4_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE00F, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconDelete: if(invert) { - drawchar(x, y, 0xE006, 159, 47, 31, alpha); - drawchar(x, y, 0xE005, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE006, 0x9F2F1F_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE005, 0x000000_rgb .WithAlpha(alpha)); } else { - drawchar(x, y, 0xE006, 159, 47, 31, alpha); - drawchar(x, y, 0xE005, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE006, 0x9F2F1F_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE005, 0xFFFFFF_rgb .WithAlpha(alpha)); } break; case IconAdd: if(invert) { - drawchar(x, y, 0xE006, 32, 144, 32, alpha); - drawchar(x, y, 0xE009, 0, 0, 0, alpha); + BlendChar({ x, y }, 0xE006, 0x209020_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE009, 0x000000_rgb .WithAlpha(alpha)); } else { - drawchar(x, y, 0xE006, 32, 144, 32, alpha); - drawchar(x, y, 0xE009, 255, 255, 255, alpha); + BlendChar({ x, y }, 0xE006, 0x209020_rgb .WithAlpha(alpha)); + BlendChar({ x, y }, 0xE009, 0xFFFFFF_rgb .WithAlpha(alpha)); } break; case IconVelocity: - drawchar(x+1, y, 0xE018, 128, 160, 255, alpha); + BlendChar({ x + 1, y }, 0xE018, 0x80A0FF_rgb .WithAlpha(alpha)); break; case IconPressure: if(invert) - drawchar(x+1, y+1, 0xE019, 180, 160, 16, alpha); + BlendChar({ x + 1, y + 1 }, 0xE019, 0xB4A010_rgb .WithAlpha(alpha)); else - drawchar(x+1, y+1, 0xE019, 255, 212, 32, alpha); + BlendChar({ x + 1, y + 1 }, 0xE019, 0xFFD420_rgb .WithAlpha(alpha)); break; case IconPersistant: if(invert) - drawchar(x+1, y+1, 0xE01A, 20, 20, 20, alpha); + BlendChar({ x + 1, y + 1 }, 0xE01A, 0x141414_rgb .WithAlpha(alpha)); else - drawchar(x+1, y+1, 0xE01A, 212, 212, 212, alpha); + BlendChar({ x + 1, y + 1 }, 0xE01A, 0xD4D4D4_rgb .WithAlpha(alpha)); break; case IconFire: - drawchar(x+1, y+1, 0xE01B, 255, 0, 0, alpha); - drawchar(x+1, y+1, 0xE01C, 255, 255, 64, alpha); + BlendChar({ x + 1, y + 1 }, 0xE01B, 0xFF0000_rgb .WithAlpha(alpha)); + BlendChar({ x + 1, y + 1 }, 0xE01C, 0xFFFF40_rgb .WithAlpha(alpha)); break; case IconBlob: if(invert) - drawchar(x+1, y, 0xE03F, 55, 180, 55, alpha); + BlendChar({ x + 1, y }, 0xE03F, 0x37B437_rgb .WithAlpha(alpha)); else - drawchar(x+1, y, 0xE03F, 55, 255, 55, alpha); + BlendChar({ x + 1, y }, 0xE03F, 0x37FF37_rgb .WithAlpha(alpha)); break; case IconHeat: - drawchar(x+3, y, 0xE03E, 255, 0, 0, alpha); + BlendChar({ x + 3, y }, 0xE03E, 0xFF0000_rgb .WithAlpha(alpha)); if(invert) - drawchar(x+3, y, 0xE03D, 0, 0, 0, alpha); + BlendChar({ x + 3, y }, 0xE03D, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x+3, y, 0xE03D, 255, 255, 255, alpha); + BlendChar({ x + 3, y }, 0xE03D, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconBlur: if(invert) - drawchar(x+1, y, 0xE044, 50, 70, 180, alpha); + BlendChar({ x + 1, y }, 0xE044, 0x3246B4_rgb .WithAlpha(alpha)); else - drawchar(x+1, y, 0xE044, 100, 150, 255, alpha); + BlendChar({ x + 1, y }, 0xE044, 0x6496FF_rgb .WithAlpha(alpha)); break; case IconGradient: if(invert) - drawchar(x+1, y+1, 0xE053, 255, 50, 255, alpha); + BlendChar({ x + 1, y + 1 }, 0xE053, 0xFF32FF_rgb .WithAlpha(alpha)); else - drawchar(x+1, y+1, 0xE053, 205, 50, 205, alpha); + BlendChar({ x + 1, y + 1 }, 0xE053, 0xCD32CD_rgb .WithAlpha(alpha)); break; case IconLife: if(invert) - drawchar(x, y+1, 0xE060, 0, 0, 0, alpha); + BlendChar({ x, y + 1 }, 0xE060, 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y+1, 0xE060, 255, 255, 255, alpha); + BlendChar({ x, y + 1 }, 0xE060, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconEffect: - drawchar(x+1, y, 0xE061, 255, 255, 160, alpha); + BlendChar({ x + 1, y }, 0xE061, 0xFFFFA0_rgb .WithAlpha(alpha)); break; case IconGlow: - drawchar(x+1, y, 0xE05F, 200, 255, 255, alpha); + BlendChar({ x + 1, y }, 0xE05F, 0xC8FFFF_rgb .WithAlpha(alpha)); break; case IconWarp: - drawchar(x+1, y, 0xE05E, 255, 255, 255, alpha); + BlendChar({ x + 1, y }, 0xE05E, 0xFFFFFF_rgb .WithAlpha(alpha)); break; case IconBasic: if(invert) - drawchar(x+1, y+1, 0xE05B, 50, 50, 0, alpha); + BlendChar({ x + 1, y + 1 }, 0xE05B, 0x323200_rgb .WithAlpha(alpha)); else - drawchar(x+1, y+1, 0xE05B, 255, 255, 200, alpha); + BlendChar({ x + 1, y + 1 }, 0xE05B, 0xFFFFC8_rgb .WithAlpha(alpha)); break; case IconAltAir: if(invert) { - drawchar(x+1, y+1, 0xE054, 180, 55, 55, alpha); - drawchar(x+1, y+1, 0xE055, 55, 180, 55, alpha); + BlendChar({ x + 1, y + 1 }, 0xE054, 0xB43737_rgb .WithAlpha(alpha)); + BlendChar({ x + 1, y + 1 }, 0xE055, 0x37B437_rgb .WithAlpha(alpha)); } else { - drawchar(x+1, y+1, 0xE054, 255, 55, 55, alpha); - drawchar(x+1, y+1, 0xE055, 55, 255, 55, alpha); + BlendChar({ x + 1, y + 1 }, 0xE054, 0xFF3737_rgb .WithAlpha(alpha)); + BlendChar({ x + 1, y + 1 }, 0xE055, 0x37FF37_rgb .WithAlpha(alpha)); } break; default: if(invert) - drawchar(x, y, 't', 0, 0, 0, alpha); + BlendChar({ x, y }, 't', 0x000000_rgb .WithAlpha(alpha)); else - drawchar(x, y, 't', 255, 255, 255, alpha); + BlendChar({ x, y }, 't', 0xFFFFFF_rgb .WithAlpha(alpha)); break; } } -void Graphics::draw_rgba_image(const pixel *data, int w, int h, int x, int y, float alpha) -{ - for (int j = 0; j < h; j++) - { - for (int i = 0; i < w; i++) - { - auto rgba = *(data++); - auto a = (rgba >> 24) & 0xFF; - auto r = (rgba >> 16) & 0xFF; - auto g = (rgba >> 8) & 0xFF; - auto b = (rgba ) & 0xFF; - addpixel(x+i, y+j, r, g, b, (int)(a*alpha)); - } - } -} - VideoBuffer Graphics::DumpFrame() { - VideoBuffer newBuffer(WINDOWW, WINDOWH); - std::copy(vid, vid+(WINDOWW*WINDOWH), newBuffer.Buffer); + VideoBuffer newBuffer(video.Size()); + std::copy_n(video.data(), video.Size().X * video.Size().Y, newBuffer.Data()); return newBuffer; } -void Graphics::SetClipRect(int &x, int &y, int &w, int &h) +void Graphics::SwapClipRect(Rect &rect) { - int newX = x; - int newY = y; - int newW = w; - int newH = h; - if (newX < 0) newX = 0; - if (newY < 0) newY = 0; - if (newW > WINDOWW - newX) newW = WINDOWW - newX; - if (newH > WINDOWH - newY) newH = WINDOWH - newY; - x = clipx1; - y = clipy1; - w = clipx2 - clipx1; - h = clipy2 - clipy1; - clipx1 = newX; - clipy1 = newY; - clipx2 = newX + newW; - clipy2 = newY + newH; -} - -bool VideoBuffer::WritePNG(const ByteString &path) const -{ - std::vector rowPointers(Height); - for (auto y = 0; y < Height; ++y) - { - rowPointers[y] = (png_const_bytep)&Buffer[y * Width]; - } -#ifdef WIN - FILE *f = _wfopen(Platform::WinWiden(path).c_str(), L"wb"); -#else - FILE *f = fopen(path.c_str(), "wb"); -#endif - if (!f) - { - std::cerr << "WritePNG: fopen failed" << std::endl; - return false; - } - png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png) - { - std::cerr << "WritePNG: png_create_write_struct failed" << std::endl; - fclose(f); - return false; - } - png_infop info = png_create_info_struct(png); - if (!info) - { - std::cerr << "WritePNG: png_create_info_struct failed" << std::endl; - png_destroy_write_struct(&png, (png_infopp)NULL); - fclose(f); - return false; - } - if (setjmp(png_jmpbuf(png))) - { - // libpng longjmp'd here in its infinite widsom, clean up and return - std::cerr << "WritePNG: longjmp from within libpng" << std::endl; - png_destroy_write_struct(&png, &info); - fclose(f); - return false; - } - png_init_io(png, f); - png_set_IHDR(png, info, Width, Height, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); - png_write_info(png, info); - png_set_filler(png, 0, PNG_FILLER_AFTER); - png_set_bgr(png); - png_write_image(png, (png_bytepp)&rowPointers[0]); - png_write_end(png, NULL); - png_destroy_write_struct(&png, &info); - fclose(f); - return true; -} - -bool PngDataToPixels(std::vector &imageData, int &imgw, int &imgh, const char *pngData, size_t pngDataSize, bool addBackground) -{ - std::vector rowPointers; - struct InMemoryFile - { - png_const_bytep data; - size_t size; - size_t cursor; - } imf{ (png_const_bytep)pngData, pngDataSize, 0 }; - png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); - if (!png) - { - std::cerr << "pngDataToPixels: png_create_read_struct failed" << std::endl; - return false; - } - png_infop info = png_create_info_struct(png); - if (!info) - { - std::cerr << "pngDataToPixels: png_create_info_struct failed" << std::endl; - png_destroy_read_struct(&png, (png_infopp)NULL, (png_infopp)NULL); - return false; - } - if (setjmp(png_jmpbuf(png))) - { - // libpng longjmp'd here in its infinite widsom, clean up and return - std::cerr << "pngDataToPixels: longjmp from within libpng" << std::endl; - png_destroy_read_struct(&png, &info, (png_infopp)NULL); - return false; - } - png_set_read_fn(png, (png_voidp)&imf, [](png_structp png, png_bytep data, size_t length) -> void { - auto ud = png_get_io_ptr(png); - auto &imf = *(InMemoryFile *)ud; - if (length + imf.cursor > imf.size) - { - png_error(png, "pngDataToPixels: libpng tried to read beyond the buffer"); - } - std::copy(imf.data + imf.cursor, imf.data + imf.cursor + length, data); - imf.cursor += length; - }); - png_set_user_limits(png, 1000, 1000); - png_read_info(png, info); - imgw = png_get_image_width(png, info); - imgh = png_get_image_height(png, info); - int bitDepth = png_get_bit_depth(png, info); - int colorType = png_get_color_type(png, info); - imageData.resize(imgw * imgh); - rowPointers.resize(imgh); - for (auto y = 0; y < imgh; ++y) - { - rowPointers[y] = (png_const_bytep)&imageData[y * imgw]; - } - if (setjmp(png_jmpbuf(png))) - { - // libpng longjmp'd here in its infinite widsom, clean up and return - std::cerr << "pngDataToPixels: longjmp from within libpng" << std::endl; - png_destroy_read_struct(&png, &info, (png_infopp)NULL); - return false; - } - if (addBackground) - { - png_set_filler(png, 0, PNG_FILLER_AFTER); - } - png_set_bgr(png); - if (colorType == PNG_COLOR_TYPE_PALETTE) - { - png_set_palette_to_rgb(png); - } - if (colorType == PNG_COLOR_TYPE_GRAY && bitDepth < 8) - { - png_set_expand_gray_1_2_4_to_8(png); - } - if (png_get_valid(png, info, PNG_INFO_tRNS)) - { - png_set_tRNS_to_alpha(png); - } - if (bitDepth == 16) - { - png_set_scale_16(png); - } - if (colorType == PNG_COLOR_TYPE_GRAY || colorType == PNG_COLOR_TYPE_GRAY_ALPHA) - { - png_set_gray_to_rgb(png); - } - if (addBackground) - { - png_color_16 defaultBackground; - defaultBackground.red = 0; - defaultBackground.green = 0; - defaultBackground.blue = 0; - png_set_background(png, &defaultBackground, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0); - } - png_read_image(png, (png_bytepp)&rowPointers[0]); - png_destroy_read_struct(&png, &info, (png_infopp)NULL); - return true; + std::swap(clipRect, rect); + clipRect &= video.Size().OriginRect(); } bool Graphics::GradientStop::operator <(const GradientStop &other) const @@ -919,9 +462,9 @@ bool Graphics::GradientStop::operator <(const GradientStop &other) const return point < other.point; } -std::vector Graphics::Gradient(std::vector stops, int resolution) +std::vector> Graphics::Gradient(std::vector stops, int resolution) { - std::vector table(resolution, 0); + std::vector> table(resolution, 0x000000_rgb); if (stops.size() >= 2) { std::sort(stops.begin(), stops.end()); @@ -940,11 +483,7 @@ std::vector Graphics::Gradient(std::vector stops, int resol auto &left = stops[stop]; auto &right = stops[stop + 1]; auto f = (point - left.point) / (right.point - left.point); - table[i] = PIXRGB( - int(int(PIXR(left.color)) + (int(PIXR(right.color)) - int(PIXR(left.color))) * f), - int(int(PIXG(left.color)) + (int(PIXG(right.color)) - int(PIXG(left.color))) * f), - int(int(PIXB(left.color)) + (int(PIXB(right.color)) - int(PIXB(left.color))) * f) - ); + table[i] = left.color.Blend(right.color.WithAlpha(uint8_t(f * 0xFF))); } } return table; diff --git a/src/graphics/Graphics.h b/src/graphics/Graphics.h index b12c2bbfd..da83f028d 100644 --- a/src/graphics/Graphics.h +++ b/src/graphics/Graphics.h @@ -1,142 +1,102 @@ -#ifndef GRAPHICS_H -#define GRAPHICS_H -#include "Config.h" - +#pragma once +#include +#include +#include +#include "common/Plane.h" #include "common/String.h" #include "common/tpt-inline.h" -#include "Pixel.h" #include "Icons.h" +#include "Pixel.h" +#include "RasterDrawMethods.h" +#include "SimulationConfig.h" -//"Graphics lite" - slightly lower performance due to variable size, -class VideoBuffer +class VideoBuffer: public RasterDrawMethods { + PlaneAdapter> video; + + Rect GetClipRect() const + { + return video.Size().OriginRect(); + } + + friend struct RasterDrawMethods; + public: - pixel * Buffer; - int Width, Height; + VideoBuffer(pixel const *data, Vec2 size); + VideoBuffer(pixel const *data, Vec2 size, size_t rowStride); + VideoBuffer(Vec2 size); + + Vec2 Size() const + { + return video.Size(); + } + + pixel *Data() + { + return video.data(); + } + + pixel const *Data() const + { + return video.data(); + } + + void Crop(Rect); - VideoBuffer(const VideoBuffer & old); - VideoBuffer(VideoBuffer * old); - VideoBuffer(pixel * buffer, int width, int height, int pitch = 0); - VideoBuffer(int width, int height); void Resize(float factor, bool resample = false); - void Resize(int width, int height, bool resample = false, bool fixedRatio = true); - void Crop(int width, int height, int x, int y); - TPT_INLINE void BlendPixel(int x, int y, int r, int g, int b, int a) - { - pixel t; - if (x<0 || y<0 || x>=Width || y>=Height) - return; - if (a!=255) - { - t = Buffer[y*(Width)+x]; - r = (a*r + (255-a)*PIXR(t)) >> 8; - g = (a*g + (255-a)*PIXG(t)) >> 8; - b = (a*b + (255-a)*PIXB(t)) >> 8; - } - Buffer[y*(Width)+x] = PIXRGB(r,g,b); - } + void Resize(Vec2 size, bool resample = false); + // Automatically choose a size to fit within the given box, keeping aspect ratio + void ResizeToFit(Vec2 bound, bool resample = false); - TPT_INLINE void SetPixel(int x, int y, int r, int g, int b, int a) - { - if (x<0 || y<0 || x>=Width || y>=Height) - return; - Buffer[y*(Width)+x] = PIXRGB((r*a)>>8, (g*a)>>8, (b*a)>>8); - } - - TPT_INLINE void AddPixel(int x, int y, int r, int g, int b, int a) - { - pixel t; - if (x<0 || y<0 || x>=Width || y>=Height) - return; - t = Buffer[y*(Width)+x]; - r = (a*r + 255*PIXR(t)) >> 8; - g = (a*g + 255*PIXG(t)) >> 8; - b = (a*b + 255*PIXB(t)) >> 8; - if (r>255) - r = 255; - if (g>255) - g = 255; - if (b>255) - b = 255; - Buffer[y*(Width)+x] = PIXRGB(r,g,b); - } - int SetCharacter(int x, int y, String::value_type c, int r, int g, int b, int a); - int BlendCharacter(int x, int y, String::value_type c, int r, int g, int b, int a); - int AddCharacter(int x, int y, String::value_type c, int r, int g, int b, int a); - ~VideoBuffer(); - - void CopyData(pixel * buffer, int width, int height, int pitch); - bool WritePNG(const ByteString &path) const; + static std::unique_ptr FromPNG(std::vector const &); + std::unique_ptr> ToPNG() const; + std::vector ToPPM() const; }; -class Graphics +class Graphics: public RasterDrawMethods { - int clipx1 = 0; - int clipy1 = 0; - int clipx2 = WINDOWW; - int clipy2 = WINDOWH; + PlaneAdapter, WINDOW.X, WINDOW.Y> video; + Rect clipRect = video.Size().OriginRect(); + + friend struct RasterDrawMethods; public: - pixel *vid; - int sdl_scale; + Vec2 Size() const + { + return video.Size(); + } + + pixel const *Data() const + { + return video.data(); + } + + pixel *Data() + { + return video.data(); + } struct GradientStop { - pixel color; + RGB color; float point; bool operator <(const GradientStop &other) const; }; - static std::vector Gradient(std::vector stops, int resolution); - - //PTIF methods - static pixel *resample_img_nn(pixel *src, int sw, int sh, int rw, int rh); - static pixel *resample_img(pixel *src, int sw, int sh, int rw, int rh); - - //Font/text metrics - static int CharWidth(String::value_type c); - static int textwidthx(const String &s, int w); - static int textwidth(const String &s); - static void textsize(const String &s, int & width, int & height); + static std::vector> Gradient(std::vector stops, int resolution); VideoBuffer DumpFrame(); - void blendpixel(int x, int y, int r, int g, int b, int a); - void addpixel(int x, int y, int r, int g, int b, int a); - void draw_icon(int x, int y, Icon icon, unsigned char alpha = 255, bool invert = false); - void Clear(); void Finalise(); - // - int drawtext_outline(int x, int y, const String &s, int r, int g, int b, int a); - int drawtext(int x, int y, const String &s, int r, int g, int b, int a); - int drawchar(int x, int y, String::value_type c, int r, int g, int b, int a); - int addchar(int x, int y, String::value_type c, int r, int g, int b, int a); - - void xor_pixel(int x, int y); - void xor_line(int x, int y, int x2, int y2); - void xor_rect(int x, int y, int width, int height); - void xor_bitmap(unsigned char * bitmap, int x, int y, int w, int h); - - void draw_line(int x, int y, int x2, int y2, int r, int g, int b, int a); - void drawrect(int x, int y, int width, int height, int r, int g, int b, int a); - void fillrect(int x, int y, int width, int height, int r, int g, int b, int a); - void drawcircle(int x, int y, int rx, int ry, int r, int g, int b, int a); - void fillcircle(int x, int y, int rx, int ry, int r, int g, int b, int a); - void clearrect(int x, int y, int width, int height); - void gradientrect(int x, int y, int width, int height, int r, int g, int b, int a, int r2, int g2, int b2, int a2); - - void draw_image(const pixel *img, int x, int y, int w, int h, int a); - void draw_image(const VideoBuffer * vidBuf, int x, int y, int a); - void draw_rgba_image(const pixel *data, int w, int h, int x, int y, float alpha); Graphics(); - ~Graphics(); - void SetClipRect(int &x, int &y, int &w, int &h); + void SwapClipRect(Rect &); + + Rect GetClipRect() const + { + return clipRect; + } }; - -bool PngDataToPixels(std::vector &imageData, int &imgw, int &imgh, const char *pngData, size_t pngDataSize, bool addBackground); - -#endif diff --git a/src/graphics/Icons.h b/src/graphics/Icons.h index 8f3dc68d4..6f65fdad5 100644 --- a/src/graphics/Icons.h +++ b/src/graphics/Icons.h @@ -1,6 +1,4 @@ -#ifndef ICONS_H -#define ICONS_H - +#pragma once //Icon names, see Graphics::draw_icon enum Icon { @@ -42,5 +40,3 @@ enum Icon IconLife, IconGradient }; - -#endif // ICONS_H diff --git a/src/graphics/Pixel.h b/src/graphics/Pixel.h index 7bbcdfa21..cce7336a9 100644 --- a/src/graphics/Pixel.h +++ b/src/graphics/Pixel.h @@ -1,43 +1,150 @@ -#ifndef PIXEL_H -#define PIXEL_H +#pragma once -#define PIXELCHANNELS 3 -#ifdef PIX16 -# define PIXELSIZE 2 -# define PIXPACK(x) ((((x)>>8)&0xF800)|(((x)>>5)&0x07E0)|(((x)>>3)&0x001F)) //16bit RGB in 16bit int: ???? -# define PIXRGB(r,g,b) ((((r)<<8)&0xF800)|(((g)<<3)&0x07E0)|(((b)>>3)&0x001F)) -# define PIXR(x) (((x)>>8)&0xF8) -# define PIXG(x) (((x)>>3)&0xFC) -# define PIXB(x) (((x)<<3)&0xF8) -#else -# define PIXELSIZE 4 -# ifdef PIX32BGRA -# define PIXPACK(x) ((((x)>>16)&0x0000FF)|((x)&0x00FF00)|(((x)<<16)&0xFF0000)) //24bit BGR in 32bit int: 00BBGGRR -# define PIXRGB(r,g,b) (((b)<<16)|((g)<<8)|((r)))// (((b)<<16)|((g)<<8)|(r)) -# define PIXR(x) ((x)&0xFF) -# define PIXG(x) (((x)>>8)&0xFF) -# define PIXB(x) ((x)>>16) -# else -# ifdef PIX32BGRA -# define PIXPACK(x) ((((x)>>8)&0x0000FF00)|(((x)<<8)&0x00FF0000)|(((x)<<24)&0xFF000000)) //32bit BGRA in 32bit int: BBGGRRAA -# define PIXRGB(r,g,b) (((b)<<24)|((g)<<16)|((r)<<8)) -# define PIXR(x) (((x)>>8)&0xFF) -# define PIXG(x) (((x)>>16)&0xFF) -# define PIXB(x) (((x)>>24)&0xFF) -# else -# define PIXPACK(x) (x) //24bit RGB in 32bit int: 00RRGGBB. -# define PIXRGB(r,g,b) (((r)<<16)|((g)<<8)|(b)) -# define PIXR(x) (((x)>>16)&0xFF) -# define PIXG(x) (((x)>>8)&0xFF) -# define PIXB(x) ((x)&0xFF) -# endif -# endif -#endif +#include +#include +#include +#include +#include -#ifdef PIX16 -typedef unsigned short pixel; -#else -typedef unsigned int pixel; -#endif +// This is always packed with the least significant byte being blue, +// then green, then red, then 0. +typedef uint32_t pixel; -#endif // PIXEL_H +constexpr int PIXELCHANNELS = 3; + +// Least significant byte is blue, then green, then red, then alpha. +// Use sparingly, e.g. when passing packed data to a third party library. +typedef uint32_t pixel_rgba; + +template>> +struct RGBA; + +template>> +struct alignas(alignof(uint32_t) > alignof(T) ? alignof(uint32_t) : alignof(T)) RGB +{ + T Blue, Green, Red; + + constexpr RGB(T r, T g, T b): + Blue(b), + Green(g), + Red(r) + { + } + + template // Disallow brace initialization + RGB(std::initializer_list) = delete; + + // Blend and Add get called in tight loops so it's important that they + // vectorize well. + template>> + constexpr RGB Blend(RGBA other) const + { + if (other.Alpha == 0xFF) + return other.NoAlpha(); + // Dividing by 0xFF means the two branches return the same value in the + // case that other.Alpha == 0xFF, and the division happens via + // multiplication and bitshift anyway, so it vectorizes better than code + // that branches in a meaningful way. + return RGB( + // the intermediate is guaranteed to fit in 16 bits, and a 16 bit + // multiplication vectorizes better than a longer one. + uint16_t(other.Alpha * other.Red + (0xFF - other.Alpha) * Red ) / 0xFF, + uint16_t(other.Alpha * other.Green + (0xFF - other.Alpha) * Green) / 0xFF, + uint16_t(other.Alpha * other.Blue + (0xFF - other.Alpha) * Blue ) / 0xFF + ); + } + + template>> + constexpr RGB Add(RGBA other) const + { + return RGB( + std::min(0xFF, Red + uint16_t(other.Alpha * other.Red) / 0xFF), + std::min(0xFF, Green + uint16_t(other.Alpha * other.Green) / 0xFF), + std::min(0xFF, Blue + uint16_t(other.Alpha * other.Blue) / 0xFF) + ); + } + + // Decrement each component that is nonzero. + template>> + constexpr RGB Decay() const + { + // This vectorizes really well. + pixel colour = Pack(), mask = colour; + mask |= mask >> 4; + mask |= mask >> 2; + mask |= mask >> 1; + mask &= 0x00010101; + return Unpack(colour - mask); + } + + template>> + RGB Inverse() const + { + return RGB(0xFF - Red, 0xFF - Green, 0xFF - Blue); + } + + constexpr RGBA WithAlpha(T a) const + { + return RGBA(Red, Green, Blue, a); + } + + template>> + constexpr pixel Pack() const + { + return Red << 16 | Green << 8 | Blue; + } + + template>> + constexpr static RGB Unpack(pixel px) + { + return RGB(px >> 16, px >> 8, px); + } +}; + +constexpr inline RGB operator ""_rgb(unsigned long long value) +{ + return RGB::Unpack(value); +} + +template +struct alignas(alignof(uint32_t) > alignof(T) ? alignof(uint32_t) : alignof(T)) RGBA +{ + T Blue, Green, Red, Alpha; + + constexpr RGBA(T r, T g, T b, T a): + Blue(b), + Green(g), + Red(r), + Alpha(a) + { + } + + template>> + RGBA(T r, T g, T b): + Blue(b), + Green(g), + Red(r), + Alpha(0xFF) + { + } + + template // Disallow brace initialization + RGBA(std::initializer_list) = delete; + + constexpr RGB NoAlpha() const + { + return RGB(Red, Green, Blue); + } + + template>> + constexpr pixel Pack() const + { + return Red << 16 | Green << 8 | Blue | Alpha << 24; + } + + template>> + constexpr static RGBA Unpack(pixel_rgba px) + { + return RGBA(px >> 16, px >> 8, px, px >> 24); + } +}; diff --git a/src/graphics/RasterDrawMethods.h b/src/graphics/RasterDrawMethods.h new file mode 100644 index 000000000..2172f8084 --- /dev/null +++ b/src/graphics/RasterDrawMethods.h @@ -0,0 +1,62 @@ +#pragma once +#include "common/String.h" +#include "common/Vec2.h" +#include "graphics/Pixel.h" + +class VideoBuffer; + +// This is a mixin that adds methods to the Derived class, using the "Curiously +// Recurring Template Pattern" trick. +template +struct RasterDrawMethods +{ + void DrawPixel(Vec2, RGB); + void BlendPixel(Vec2, RGBA); + void AddPixel(Vec2, RGBA); + void XorPixel(Vec2); + + void DrawLine(Vec2, Vec2, RGB); + void BlendLine(Vec2, Vec2, RGBA); + void AddLine(Vec2, Vec2, RGBA); + void XorLine(Vec2, Vec2); + + void DrawRect(Rect, RGB); + void BlendRect(Rect, RGBA); + + void XorDottedRect(Rect); + + void DrawFilledRect(Rect, RGB); + void BlendFilledRect(Rect, RGBA); + + void BlendEllipse(Vec2 center, Vec2 size, RGBA); + + void BlendFilledEllipse(Vec2 center, Vec2 size, RGBA); + + void BlendImage(pixel const *, uint8_t alpha, Rect); + void BlendImage(pixel const *, uint8_t alpha, Rect, size_t rowStride); + void XorImage(unsigned char const *, Rect); + void XorImage(unsigned char const *, Rect, size_t rowStride); + + void BlendRGBAImage(pixel_rgba const *, Rect); + void BlendRGBAImage(pixel_rgba const *, Rect, size_t rowStride); + + // Returns width of character + int BlendChar(Vec2, String::value_type, RGBA); + int AddChar(Vec2, String::value_type, RGBA); + + // Returns the offset between the first character and the + // would-be-next character + Vec2 BlendText(Vec2, String const &, RGBA); + + Vec2 BlendTextOutline(Vec2, String const &, RGBA); + + static int CharWidth(String::value_type); + // Considers the first line to be FONT_H-2 tall with successive lines adding + // FONT_H each + static Vec2 TextSize(String const &); + // Return iterator to the end of an initial portion of text that fits in + // the given width + static String::const_iterator TextFit(String const &, int width); + + void Clear(); +}; diff --git a/src/graphics/RasterDrawMethods.inl b/src/graphics/RasterDrawMethods.inl deleted file mode 100644 index c4f35154b..000000000 --- a/src/graphics/RasterDrawMethods.inl +++ /dev/null @@ -1,498 +0,0 @@ -#include -#include "FontReader.h" - -int PIXELMETHODS_CLASS::drawtext_outline(int x, int y, const String &s, int r, int g, int b, int a) -{ - drawtext(x-1, y-1, s, 0, 0, 0, 120); - drawtext(x+1, y+1, s, 0, 0, 0, 120); - - drawtext(x-1, y+1, s, 0, 0, 0, 120); - drawtext(x+1, y-1, s, 0, 0, 0, 120); - - return drawtext(x, y, s, r, g, b, a); -} - -int PIXELMETHODS_CLASS::drawtext(int x, int y, const String &str, int r, int g, int b, int a) -{ - if(!str.size()) - return 0; - - bool underline = false; - int invert = 0; - int oR = r, oG = g, oB = b; - int characterX = x, characterY = y; - int startX = characterX; - for (size_t i = 0; i < str.length(); i++) - { - if (str[i] == '\n') - { - characterX = startX; - characterY += FONT_H; - } - else if (str[i] == '\x0F') - { - if (str.length() <= i+3) - break; - oR = r; - oG = g; - oB = b; - r = (unsigned char)str[i + 1]; - g = (unsigned char)str[i + 2]; - b = (unsigned char)str[i + 3]; - i += 3; - } - else if (str[i] == '\x0E') - { - r = oR; - g = oG; - b = oB; - } - else if (str[i] == '\x01') - { - invert = !invert; - r = 255-r; - g = 255-g; - b = 255-b; - } - else if (str[i] == '\b') - { - if (str.length() <= i + 1) - break; - auto colorCode = false; - switch (str[i + 1]) - { - case 'U': underline = !underline; break; - case 'w': r = 255; g = 255; b = 255; colorCode = true; break; - case 'g': r = 192; g = 192; b = 192; colorCode = true; break; - case 'o': r = 255; g = 216; b = 32; colorCode = true; break; - case 'r': r = 255; g = 0; b = 0; colorCode = true; break; - case 'l': r = 255; g = 75; b = 75; colorCode = true; break; - case 'b': r = 0; g = 0; b = 255; colorCode = true; break; - case 't': b = 255; g = 170; r = 32; colorCode = true; break; - case 'u': r = 147; g = 83; b = 211; colorCode = true; break; - } - if (colorCode && invert) - { - r = 255-r; - g = 255-g; - b = 255-b; - } - i++; - } - else - { - auto newCharacterX = drawchar(characterX, characterY, str[i], r, g, b, a); - if (underline) - { - for (int i = characterX; i < newCharacterX; ++i) - { - blendpixel(i, y + FONT_H, r, g, b, a); - } - } - characterX = newCharacterX; - } - } - return x; -} - -int PIXELMETHODS_CLASS::drawchar(int x, int y, String::value_type c, int r, int g, int b, int a) -{ - FontReader reader(c); - for (int j = -2; j < FONT_H - 2; j++) - for (int i = 0; i < reader.GetWidth(); i++) - blendpixel(x + i, y + j, r, g, b, reader.NextPixel() * a / 3); - return x + reader.GetWidth(); -} - -int PIXELMETHODS_CLASS::addchar(int x, int y, String::value_type c, int r, int g, int b, int a) -{ - FontReader reader(c); - for (int j = -2; j < FONT_H - 2; j++) - for (int i = 0; i < reader.GetWidth(); i++) - addpixel(x + i, y + j, r, g, b, reader.NextPixel() * a / 3); - return x + reader.GetWidth(); -} - -TPT_INLINE void PIXELMETHODS_CLASS::xor_pixel(int x, int y) -{ - int c; -#ifdef DO_CLIPCHECK - if (x=clipx2 || y>=clipy2) -#else - if (x<0 || y<0 || x>=VIDXRES || y>=VIDYRES) -#endif - return; - c = vid[y*(VIDXRES)+x]; - c = PIXB(c) + 3*PIXG(c) + 2*PIXR(c); - if (c<512) - vid[y*(VIDXRES)+x] = PIXPACK(0xC0C0C0); - else - vid[y*(VIDXRES)+x] = PIXPACK(0x404040); -} - -void PIXELMETHODS_CLASS::blendpixel(int x, int y, int r, int g, int b, int a) -{ - pixel t; -#ifdef DO_CLIPCHECK - if (x=clipx2 || y>=clipy2) -#else - if (x<0 || y<0 || x>=VIDXRES || y>=VIDYRES) -#endif - return; - if (a!=255) - { - t = vid[y*(VIDXRES)+x]; - r = (a*r + (255-a)*PIXR(t)) >> 8; - g = (a*g + (255-a)*PIXG(t)) >> 8; - b = (a*b + (255-a)*PIXB(t)) >> 8; - } - vid[y*(VIDXRES)+x] = PIXRGB(r,g,b); -} - -void PIXELMETHODS_CLASS::addpixel(int x, int y, int r, int g, int b, int a) -{ - pixel t; -#ifdef DO_CLIPCHECK - if (x=clipx2 || y>=clipy2) -#else - if (x<0 || y<0 || x>=VIDXRES || y>=VIDYRES) -#endif - return; - t = vid[y*(VIDXRES)+x]; - r = (a*r + 255*PIXR(t)) >> 8; - g = (a*g + 255*PIXG(t)) >> 8; - b = (a*b + 255*PIXB(t)) >> 8; - if (r>255) - r = 255; - if (g>255) - g = 255; - if (b>255) - b = 255; - vid[y*(VIDXRES)+x] = PIXRGB(r,g,b); -} - -void PIXELMETHODS_CLASS::xor_line(int x1, int y1, int x2, int y2) -{ - int cp=abs(y2-y1)>abs(x2-x1), x, y, dx, dy, sy; - float e, de; - if (cp) - { - y = x1; - x1 = y1; - y1 = y; - y = x2; - x2 = y2; - y2 = y; - } - if (x1 > x2) - { - y = x1; - x1 = x2; - x2 = y; - y = y1; - y1 = y2; - y2 = y; - } - dx = x2 - x1; - dy = abs(y2 - y1); - e = 0.0f; - if (dx) - de = dy/(float)dx; - else - de = 0.0f; - y = y1; - sy = (y1= 0.5f) - { - y += sy; - e -= 1.0f; - } - } -} - -void PIXELMETHODS_CLASS::xor_rect(int x, int y, int w, int h) -{ - int i; - for (i=0; iabs(x2-x1), x, y, dx, dy, sy; - float e, de; - if (cp) - { - y = x1; - x1 = y1; - y1 = y; - y = x2; - x2 = y2; - y2 = y; - } - if (x1 > x2) - { - y = x1; - x1 = x2; - x2 = y; - y = y1; - y1 = y2; - y2 = y; - } - dx = x2 - x1; - dy = abs(y2 - y1); - e = 0.0f; - if (dx) - de = dy/(float)dx; - else - de = 0.0f; - y = y1; - sy = (y1= 0.5f) - { - y += sy; - e -= 1.0f; - } - } -} - -void PIXELMETHODS_CLASS::drawrect(int x, int y, int w, int h, int r, int g, int b, int a) -{ - int i; - w--; - h--; - for (i=0; i<=w; i++) - { - blendpixel(x+i, y, r, g, b, a); - blendpixel(x+i, y+h, r, g, b, a); - } - for (i=1; i clipx2) w = clipx2-x; - if (y+h > clipy2) h = clipy2-y; - if (x VIDXRES) w = VIDXRES-x; - if (y+h > VIDYRES) h = VIDYRES-y; - if (x<0) - { - w += x; - x = 0; - } - if (y<0) - { - h += y; - y = 0; - } -#endif - if (w<0 || h<0) - return; - - for (i=0; i VIDYRES) - h = ((VIDYRES)-y)-1; - // Too big - if (x + w > VIDXRES) - return; - - // Starts off the top of the screen, adjust - if (y < 0 && -y < h) - { - img += -y*w; - h += y; - y = 0; - } - // Starts off the left side of the screen, adjust - if (x < 0 && -x < w) - { - startX = -x; - } - - if (!h || y < 0 || !w) - return; - if (a >= 255) - for (int j = 0; j < h; j++) - { - img += startX; - for (int i = startX; i < w; i++) - { -#ifdef DO_CLIPCHECK - if (!(x+i=clipx2 || y+j>=clipy2)) -#endif - vid[(y+j)*(VIDXRES)+(x+i)] = *img; - img++; - } - } - else - { - int r, g, b; - for (int j = 0; j < h; j++) - { - img += startX; - for (int i = startX; i < w; i++) - { - r = PIXR(*img); - g = PIXG(*img); - b = PIXB(*img); - blendpixel(x+i, y+j, r, g, b, a); - img++; - } - } - } -} - -void PIXELMETHODS_CLASS::draw_image(const VideoBuffer * vidBuf, int x, int y, int a) -{ - draw_image(vidBuf->Buffer, x, y, vidBuf->Width, vidBuf->Height, a); -} diff --git a/src/graphics/RasterDrawMethodsImpl.h b/src/graphics/RasterDrawMethodsImpl.h new file mode 100644 index 000000000..eb39f0d88 --- /dev/null +++ b/src/graphics/RasterDrawMethodsImpl.h @@ -0,0 +1,403 @@ +#include +#include +#include "common/RasterGeometry.h" +#include "FontReader.h" +#include "Graphics.h" +#include "RasterDrawMethods.h" + +#define clipRect() (static_cast(*this).GetClipRect()) + +template +static inline void drawPixelUnchecked(RasterDrawMethods &self, V Derived::*video, Vec2 pos, RGB colour) +{ + (static_cast(self).*video)[pos] = colour.Pack(); +} + +template +static inline void blendPixelUnchecked(RasterDrawMethods &self, V Derived::*video, Vec2 pos, RGBA colour) +{ + pixel &px = (static_cast(self).*video)[pos]; + px = RGB::Unpack(px).Blend(colour).Pack(); +} + +template +static inline void xorPixelUnchecked(RasterDrawMethods &self, V Derived::*video, Vec2 pos) +{ + pixel &px = (static_cast(self).*video)[pos]; + auto const c = RGB::Unpack(px); + if (2 * c.Red + 3 * c.Green + c.Blue < 512) + px = 0xC0C0C0_rgb .Pack(); + else + px = 0x404040_rgb .Pack(); +} + +template +inline void RasterDrawMethods::DrawPixel(Vec2 pos, RGB colour) +{ + if (clipRect().Contains(pos)) + drawPixelUnchecked(*this, &Derived::video, pos, colour); +} + +template +inline void RasterDrawMethods::BlendPixel(Vec2 pos, RGBA colour) +{ + if (clipRect().Contains(pos)) + blendPixelUnchecked(*this, &Derived::video, pos, colour); +} + +template +inline void RasterDrawMethods::AddPixel(Vec2 pos, RGBA colour) +{ + if (clipRect().Contains(pos)) + { + pixel &px = (static_cast(*this).video)[pos]; + px = RGB::Unpack(px).Add(colour).Pack(); + } +} + +template +inline void RasterDrawMethods::XorPixel(Vec2 pos) +{ + if (clipRect().Contains(pos)) + xorPixelUnchecked(*this, &Derived::video, pos); +} + +template +void RasterDrawMethods::DrawLine(Vec2 pos1, Vec2 pos2, RGB colour) +{ + RasterizeLine(pos1, pos2, [this, colour](Vec2 pos) { + DrawPixel(pos, colour); + }); +} + +template +void RasterDrawMethods::BlendLine(Vec2 pos1, Vec2 pos2, RGBA colour) +{ + RasterizeLine(pos1, pos2, [this, colour](Vec2 pos) { + BlendPixel(pos, colour); + }); +} + +template +void RasterDrawMethods::AddLine(Vec2 pos1, Vec2 pos2, RGBA colour) +{ + RasterizeLine(pos1, pos2, [this, colour](Vec2 pos) { + AddPixel(pos, colour); + }); +} + +template +void RasterDrawMethods::XorLine(Vec2 pos1, Vec2 pos2) +{ + RasterizeLine(pos1, pos2, [this](Vec2 pos) { + XorPixel(pos); + }); +} + +template +void RasterDrawMethods::DrawRect(Rect rect, RGB colour) +{ + RasterizeRect(rect, [this, colour](Vec2 pos) { + DrawPixel(pos, colour); + }); +} + +template +void RasterDrawMethods::BlendRect(Rect rect, RGBA colour) +{ + RasterizeRect(rect, [this, colour](Vec2 pos) { + BlendPixel(pos, colour); + }); +} + +template +void RasterDrawMethods::XorDottedRect(Rect rect) +{ + RasterizeDottedRect(rect, [this](Vec2 pos) { + XorPixel(pos); + }); +} + +template +void RasterDrawMethods::DrawFilledRect(Rect rect, RGB colour) +{ + rect &= clipRect(); + pixel packed = colour.Pack(); + auto &video = static_cast(*this).video; + if (rect) + for (int y = rect.TopLeft.Y; y <= rect.BottomRight.Y; y++) + std::fill_n(video.RowIterator(Vec2(rect.TopLeft.X, y)), rect.Size().X, packed); +} + +template +void RasterDrawMethods::BlendFilledRect(Rect rect, RGBA colour) +{ + for (auto pos : rect & clipRect()) + blendPixelUnchecked(*this, &Derived::video, pos, colour); +} + +template +void RasterDrawMethods::BlendEllipse(Vec2 center, Vec2 size, RGBA colour) +{ + RasterizeEllipsePoints(Vec2(float(size.X * size.X), float(size.Y * size.Y)), [this, center, colour](Vec2 delta) { + BlendPixel(center + delta, colour); + }); +} + +template +void RasterDrawMethods::BlendFilledEllipse(Vec2 center, Vec2 size, RGBA colour) +{ + RasterizeEllipseRows(Vec2(float(size.X * size.X), float(size.Y * size.Y)), [this, center, colour](int xLim, int dy) { + for (auto pos : clipRect() & RectBetween(center + Vec2(-xLim, dy), center + Vec2(xLim, dy))) + blendPixelUnchecked(*this, &Derived::video, pos, colour); + }); +} + +template +void RasterDrawMethods::BlendImage(pixel const *data, uint8_t alpha, Rect rect) +{ + BlendImage(data, alpha, rect, rect.Size().X); +} + +template +void RasterDrawMethods::BlendImage(pixel const *data, uint8_t alpha, Rect rect, size_t rowStride) +{ + auto origin = rect.TopLeft; + rect &= clipRect(); + if (alpha == 0xFF) + { + auto &video = static_cast(*this).video; + for (int y = rect.TopLeft.Y; y <= rect.BottomRight.Y; y++) + std::copy_n( + data + (rect.TopLeft.X - origin.X) + (y - origin.Y) * rowStride, + rect.Size().X, + video.RowIterator(Vec2(rect.TopLeft.X, y)) + ); + } + else + { + for (auto pos : rect) + { + pixel const px = data[(pos.X - origin.X) + (pos.Y - origin.Y) * rowStride]; + blendPixelUnchecked(*this, &Derived::video, pos, RGB::Unpack(px).WithAlpha(alpha)); + } + } +} + +template +void RasterDrawMethods::XorImage(unsigned char const *data, Rect rect) +{ + XorImage(data, rect, rect.Size().X); +} + +template +void RasterDrawMethods::XorImage(unsigned char const *data, Rect rect, size_t rowStride) +{ + auto origin = rect.TopLeft; + rect &= clipRect(); + for (auto pos : rect) + if (data[(pos.X - origin.X) + (pos.Y - origin.Y) * rowStride]) + xorPixelUnchecked(*this, &Derived::video, pos); +} + +template +void RasterDrawMethods::BlendRGBAImage(pixel_rgba const *data, Rect rect) +{ + BlendRGBAImage(data, rect, rect.Size().X); +} + +template +void RasterDrawMethods::BlendRGBAImage(pixel_rgba const *data, Rect rect, size_t rowStride) +{ + auto origin = rect.TopLeft; + rect &= clipRect(); + for (auto pos : rect) + { + pixel const px = data[(pos.X - origin.X) + (pos.Y - origin.Y) * rowStride]; + blendPixelUnchecked(*this, &Derived::video, pos, RGBA::Unpack(px)); + } +} + +template +int RasterDrawMethods::BlendChar(Vec2 pos, String::value_type ch, RGBA colour) +{ + FontReader reader(ch); + auto const rect = RectSized(Vec2(0, -2), Vec2(reader.GetWidth(), FONT_H)); + for (auto off : rect.template Range()) + BlendPixel(pos + off, colour.NoAlpha().WithAlpha(reader.NextPixel() * colour.Alpha / 3)); + return reader.GetWidth(); +} + +template +int RasterDrawMethods::AddChar(Vec2 pos, String::value_type ch, RGBA colour) +{ + FontReader reader(ch); + RGB const c = colour.NoAlpha(); + auto const rect = RectSized(Vec2(0, -2), Vec2(reader.GetWidth(), FONT_H)); + for (auto off : rect.template Range()) + AddPixel(pos + off, c.WithAlpha(reader.NextPixel() * colour.Alpha / 3)); + return reader.GetWidth(); +} + +template +Vec2 RasterDrawMethods::BlendText(Vec2 orig_pos, String const &str, RGBA orig_colour) +{ + bool underline = false; + bool invert = false; + RGB colour = orig_colour.NoAlpha(); + uint8_t alpha = orig_colour.Alpha; + Vec2 pos = orig_pos; + for (size_t i = 0; i < str.length(); i++) + { + if (str[i] == '\n') + { + pos.X = orig_pos.X; + pos.Y += FONT_H; + } + else if (str[i] == '\x0F') + { + if (str.length() <= i + 3) + break; + colour.Red = str[i + 1]; + colour.Green = str[i + 2]; + colour.Blue = str[i + 3]; + i += 3; + } + else if (str[i] == '\x0E') + { + colour = orig_colour.NoAlpha(); + } + else if (str[i] == '\x01') + { + invert = !invert; + colour = colour.Inverse(); + } + else if (str[i] == '\b') + { + if (str.length() <= i + 1) + break; + bool colourCode = true; + switch (str[i + 1]) + { + case 'U': underline = !underline; colourCode = false; break; + case 'w': colour = 0xFFFFFF_rgb; break; + case 'g': colour = 0xC0C0C0_rgb; break; + case 'o': colour = 0xFFD820_rgb; break; + case 'r': colour = 0xFF0000_rgb; break; + case 'l': colour = 0xFF4B4B_rgb; break; + case 'b': colour = 0x0000FF_rgb; break; + case 't': colour = 0x20AAFF_rgb; break; + case 'u': colour = 0x9353D3_rgb; break; + } + if (colourCode && invert) + colour = colour.Inverse(); + i++; + } + else + { + int dx = BlendChar(pos, str[i], colour.WithAlpha(alpha)); + if (underline) + for (int i = 0; i < dx; i++) + BlendPixel(pos + Vec2(i, FONT_H), colour.WithAlpha(alpha)); + pos.X += dx; + } + } + return pos - orig_pos; +} + +template +Vec2 RasterDrawMethods::BlendTextOutline(Vec2 pos, String const &str, RGBA colour) +{ + BlendText(pos + Vec2(-1, -1), str, 0x000000_rgb .WithAlpha(0x78)); + BlendText(pos + Vec2(-1, +1), str, 0x000000_rgb .WithAlpha(0x78)); + BlendText(pos + Vec2(+1, -1), str, 0x000000_rgb .WithAlpha(0x78)); + BlendText(pos + Vec2(+1, +1), str, 0x000000_rgb .WithAlpha(0x78)); + + return BlendText(pos, str, colour); +} + +template +void RasterDrawMethods::Clear() +{ + auto &video = static_cast(*this).video; + std::fill_n(video.data(), video.Size().X * video.Size().Y, 0x000000_rgb .Pack()); +} + +template +int RasterDrawMethods::CharWidth(String::value_type ch) +{ + return FontReader(ch).GetWidth(); +} + +template +Vec2 RasterDrawMethods::TextSize(String const &str) +{ + Vec2 size = Vec2(0, FONT_H - 2); + int curX = 0; // characters have 1px of spacing between them + for (size_t i = 0; i < str.length(); i++) + { + if (str[i] == '\n') + { + size.X = std::max(curX, size.X); + size.Y += FONT_H; + curX = 0; + } + else if (str[i] == '\x0F') + { + if (str.length() <= i + 3) + break; + i += 3; + } + else if (str[i] == '\x0E') + continue; + else if (str[i] == '\x01') + continue; + else if (str[i] == '\b') + { + if (str.length() <= i + 1) + break; + i++; + } + else + curX += CharWidth(str[i]); + } + size.X = std::max(curX, size.X); + return size; +} + +template +String::const_iterator RasterDrawMethods::TextFit(String const &str, int width) +{ + int curX = 0; + for (size_t i = 0; i < str.length(); i++) + { + if (str[i] == '\n') + curX = 0; + else if (str[i] == '\x0F') + { + if (str.length() <= i + 3) + break; + i += 3; + } + else if (str[i] == '\x0E') + continue; + else if (str[i] == '\x01') + continue; + else if (str[i] == '\b') + { + if (str.length() <= i + 1) + break; + i++; + } + else + { + int dx = CharWidth(str[i]); + if (curX + dx / 2 >= width) + return str.begin() + i; + curX += dx; + } + } + return str.end(); +} + +#undef clipRect diff --git a/src/graphics/RasterGraphics.cpp b/src/graphics/RasterGraphics.cpp index 1f6156b92..22acce632 100644 --- a/src/graphics/RasterGraphics.cpp +++ b/src/graphics/RasterGraphics.cpp @@ -1,35 +1,12 @@ -#include "Graphics.h" - #include #include - -Graphics::Graphics(): -sdl_scale(1) -{ - vid = (pixel *)malloc(PIXELSIZE * (WINDOWW * WINDOWH)); - -} - -Graphics::~Graphics() -{ - free(vid); -} - -void Graphics::Clear() -{ - memset(vid, 0, PIXELSIZE * (WINDOWW * WINDOWH)); -} +#include "Graphics.h" +#include "SimulationConfig.h" +#include "RasterDrawMethodsImpl.h" void Graphics::Finalise() { } -#define VIDXRES WINDOWW -#define VIDYRES WINDOWH -#define PIXELMETHODS_CLASS Graphics -#define DO_CLIPCHECK -#include "RasterDrawMethods.inl" -#undef VIDYRES -#undef VIDXRES -#undef PIXELMETHODS_CLASS +template struct RasterDrawMethods; diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp index f158ccf9d..9e9a5742e 100644 --- a/src/graphics/Renderer.cpp +++ b/src/graphics/Renderer.cpp @@ -1,266 +1,119 @@ #include "Renderer.h" - -#include -#include -#include -#include -#include -#include -#include "Config.h" #include "Misc.h" - #include "common/tpt-rand.h" #include "common/tpt-compat.h" - -#include "gui/game/RenderPreset.h" - #include "simulation/Simulation.h" #include "simulation/ElementGraphics.h" -#include "simulation/Air.h" -#include "simulation/Gravity.h" #include "simulation/ElementClasses.h" +#include "simulation/Air.h" +#include "simulation/gravity/Gravity.h" +#include -#ifdef LUACONSOLE -#include "lua/LuaScriptInterface.h" -#include "lua/LuaScriptHelper.h" -#include "lua/LuaSmartRef.h" -#endif - -#define VIDXRES WINDOWW -#define VIDYRES WINDOWH - - -void Renderer::RenderBegin() +std::unique_ptr Renderer::WallIcon(int wallID, Vec2 size) { - if(display_mode & DISPLAY_PERS) + auto wtypes = LoadWalls(); + if (wallID < 0 || wallID >= int(wtypes.size())) + return nullptr; + wall_type const &wtype = wtypes[wallID]; + + RGB primary = wtype.colour; + RGB secondary = wtype.eglow; + + auto texture = std::make_unique(size); + switch (wtype.drawstyle) { - std::copy(persistentVid, persistentVid+(VIDXRES*YRES), vid); - } - pixel * oldVid = NULL; - if(display_mode & DISPLAY_WARP) - { - oldVid = vid; - vid = warpVid; - std::fill(warpVid, warpVid+(VIDXRES*VIDYRES), 0); + case 1: + // #.#. + // .... + // .#.# + // .... + for (auto pos : size.OriginRect()) + if (~pos.Y & ~(pos.X ^ (pos.Y >> 1)) & 1) + texture->DrawPixel(pos, primary); + break; + case 2: + // #.#. + // .... + // #.#. + // .... + for (auto pos : size.OriginRect()) + if (~pos.Y & ~pos.X & 1) + texture->DrawPixel(pos, primary); + break; + case 3: + // #### + // #### + // #### + // #### + for (auto pos : size.OriginRect()) + texture->DrawPixel(pos, primary); + break; + case 4: + // #+.# + // .#+. + // +.#+ + // #+.# + for (auto pos : size.OriginRect()) + if (((pos.X - pos.Y) % CELL + CELL) % CELL == 0) + texture->DrawPixel(pos, primary); + else if (((pos.X - pos.Y) % CELL + CELL) % CELL == 1) + texture->DrawPixel(pos, secondary); + else + texture->DrawPixel(pos, 0x202020_rgb); + break; } -#ifndef FONTEDITOR - draw_air(); - draw_grav(); - DrawWalls(); - render_parts(); - - if(display_mode & DISPLAY_PERS) + switch (wallID) { - int i,r,g,b; - for (i = 0; i < VIDXRES*YRES; i++) - { - r = PIXR(vid[i]); - g = PIXG(vid[i]); - b = PIXB(vid[i]); - if (r>0) - r--; - if (g>0) - g--; - if (b>0) - b--; - persistentVid[i] = PIXRGB(r,g,b); - } - } - - render_fire(); - draw_other(); - draw_grav_zones(); - DrawSigns(); -#endif - - if(display_mode & DISPLAY_WARP) - { - vid = oldVid; - } - - FinaliseParts(); -} - -void Renderer::RenderEnd() -{ - RenderZoom(); -} - -void Renderer::SetSample(int x, int y) -{ - sampleColor = GetPixel(x, y); -} - -void Renderer::clearScreen(float alpha) -{ - g->Clear(); -} - -void Renderer::FinaliseParts() -{ - if(display_mode & DISPLAY_WARP) - { - render_gravlensing(warpVid); - } -} - -void Renderer::RenderZoom() -{ - if(!zoomEnabled) - return; - { - int x, y, i, j; - pixel pix; - pixel * img = vid; - clearrect(zoomWindowPosition.X-1, zoomWindowPosition.Y-1, zoomScopeSize*ZFACTOR+1, zoomScopeSize*ZFACTOR+1); - drawrect(zoomWindowPosition.X-2, zoomWindowPosition.Y-2, zoomScopeSize*ZFACTOR+3, zoomScopeSize*ZFACTOR+3, 192, 192, 192, 255); - drawrect(zoomWindowPosition.X-1, zoomWindowPosition.Y-1, zoomScopeSize*ZFACTOR+1, zoomScopeSize*ZFACTOR+1, 0, 0, 0, 255); - for (j=0; j Renderer_wtypes = LoadWalls(); - int i, j; - int wt = wallID; - if (wt<0 || wt>=(int)Renderer_wtypes.size()) - return 0; - wall_type *wtypes = Renderer_wtypes.data(); - pixel pc = wtypes[wt].colour; - pixel gc = wtypes[wt].eglow; - VideoBuffer * newTexture = new VideoBuffer(width, height); - if (wtypes[wt].drawstyle==1) - { - for (j=0; j>1)&1; iSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - else if (wtypes[wt].drawstyle==2) - { - for (j=0; jSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - else if (wtypes[wt].drawstyle==3) - { - for (j=0; jSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - else if (wtypes[wt].drawstyle==4) - { - for (j=0; jSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - else if (i%CELL == (j%CELL)+1 || (i%CELL == 0 && j%CELL == CELL-1)) - newTexture->SetPixel(i, j, PIXR(gc), PIXG(gc), PIXB(gc), 255); - else - newTexture->SetPixel(i, j, 0x20, 0x20, 0x20, 255); - } - - // special rendering for some walls - if (wt==WL_EWALL) - { - for (j=0; jSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - for (; iSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - } - } - else if (wt==WL_WALLELEC) - { - for (j=0; jSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - else - newTexture->SetPixel(i, j, 0x80, 0x80, 0x80, 255); - } - } - else if (wt==WL_EHOLE || wt==WL_STASIS) - { - for (j=0; jSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - for (; iSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - } - } - else if (wt == WL_ERASE) - { - for (j=0; j>1)); iSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - } - for (j=0; jSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - } - for (j=3; j<(width-4)/2; j++) - { - newTexture->SetPixel(j+6, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(j+7, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(-j+19, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(-j+20, j, 0xFF, 0, 0, 255); - } - } - else if (wt == WL_ERASEALL) - { - for (int j = 0; j < height; j++) + case WL_EWALL: + // ##### ....... + // #.#.#. ...#.# + // ####### ..... + // #.#.#.#. .#.# + for (auto pos : size.OriginRect()) + if ((pos.X < size.X / 4 + pos.Y) != (pos.X & pos.Y & 1)) + texture->DrawPixel(pos, primary); + break; + case WL_WALLELEC: + // #+#+ + // ++++ + // #+#+ + // ++++ + for (auto pos : size.OriginRect()) + if (~pos.Y & ~pos.X & 1) + texture->DrawPixel(pos, primary); + else + texture->DrawPixel(pos, 0x808080_rgb); + break; + case WL_EHOLE: + case WL_STASIS: + // ..... ####### + // .#.#.# ###.#. + // ....... ##### + // .#.#.#.# #.#. + for (auto pos : size.OriginRect()) + if ((pos.X < size.X / 4 + pos.Y) == (pos.X & pos.Y & 1)) + texture->DrawPixel(pos, primary); + break; + case WL_ERASE: + // #.#.#. ###### + // ...... ###### + // .#.#.# ###### + // ...... ###### + for (auto pos : size.OriginRect()) + if ((pos.X < size.X / 2) ? ~pos.Y & ~(pos.X ^ (pos.Y >> 1)) & 1 : true) + texture->DrawPixel(pos, primary); + texture->BlendChar(size / 2 - Vec2(4, 2), 0xE06C, 0xFF0000_rgb .WithAlpha(0xFF)); + break; + case WL_ERASEALL: { int r = 100, g = 150, b = 50; int rd = 1, gd = -1, bd = -1; - for (int i = 0; i < width; i++) + for (int x = 0; x < size.X; x++) { - r += 15*rd; - g += 15*gd; - b += 15*bd; + r += 15 * rd; + g += 15 * gd; + b += 15 * bd; if (r > 200) rd = -1; if (g > 200) gd = -1; if (b > 200) bd = -1; @@ -270,302 +123,21 @@ VideoBuffer * Renderer::WallIcon(int wallID, int width, int height) int rc = std::min(150, std::max(0, r)); int gc = std::min(200, std::max(0, g)); int bc = std::min(200, std::max(0, b)); - newTexture->SetPixel(i, j, rc, gc, bc, 255); + texture->DrawLine(Vec2(x, 0), Vec2(x, size.Y - 1), RGB(rc, gc, bc)); } + texture->BlendChar(size / 2 - Vec2(10, 2), 0xE06C, 0xFF0000_rgb .WithAlpha(0xFF)); + texture->BlendChar(size / 2 - Vec2(-1, 2), 0xE06C, 0xFF0000_rgb .WithAlpha(0xFF)); } - for (int j = 3; j < (width-4)/2; j++) - { - newTexture->SetPixel(j+0, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(j+1, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(-j+13, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(-j+14, j, 0xFF, 0, 0, 255); - - newTexture->SetPixel(j+11, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(j+12, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(-j+24, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(-j+25, j, 0xFF, 0, 0, 255); - } + break; + case WL_STREAM: + texture->DrawRect(size.OriginRect(), 0xA0A0A0_rgb); + texture->AddChar(Vec2(4, 2), 0xE00D, 0xFFFFFF_rgb .WithAlpha(0xFF)); + texture->AddChar(Vec2(8, 2), 0xE06D, 0xFFFFFF_rgb .WithAlpha(0xFF)); + break; } - else if(wt == WL_STREAM) - { - for (j=0; jSetPixel(i, j, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - } - newTexture->AddCharacter(4, 2, 0xE00D, 255, 255, 255, 255); - for (i=width/3; iSetPixel(i, 7+(int)(3.9f*cos(i*0.3f)), 255, 255, 255, 255); - } - } - return newTexture; -} -#endif - -void Renderer::DrawBlob(int x, int y, unsigned char cr, unsigned char cg, unsigned char cb) -{ - blendpixel(x+1, y, cr, cg, cb, 112); - blendpixel(x-1, y, cr, cg, cb, 112); - blendpixel(x, y+1, cr, cg, cb, 112); - blendpixel(x, y-1, cr, cg, cb, 112); - - blendpixel(x+1, y-1, cr, cg, cb, 64); - blendpixel(x-1, y-1, cr, cg, cb, 64); - blendpixel(x+1, y+1, cr, cg, cb, 64); - blendpixel(x-1, y+1, cr, cg, cb, 64); + return texture; } -void Renderer::DrawWalls() -{ - for (int y = 0; y < YRES/CELL; y++) - for (int x =0; x < XRES/CELL; x++) - if (sim->bmap[y][x]) - { - unsigned char wt = sim->bmap[y][x]; - if (wt >= UI_WALLCOUNT) - continue; - unsigned char powered = sim->emap[y][x]; - pixel pc = PIXPACK(sim->wtypes[wt].colour); - pixel gc = PIXPACK(sim->wtypes[wt].eglow); - - if (findingElement) - { - pc = PIXRGB(PIXR(pc)/10,PIXG(pc)/10,PIXB(pc)/10); - gc = PIXRGB(PIXR(gc)/10,PIXG(gc)/10,PIXB(gc)/10); - } - - switch (sim->wtypes[wt].drawstyle) - { - case 0: - if (wt == WL_EWALL || wt == WL_STASIS) - { - bool reverse = wt == WL_STASIS; - if ((powered > 0) ^ reverse) - { - for (int j = 0; j < CELL; j++) - for (int i =0; i < CELL; i++) - if (i&j&1) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc; - } - else - { - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - if (!(i&j&1)) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc; - } - } - else if (wt == WL_WALLELEC) - { - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - { - if (!((y*CELL+j)%2) && !((x*CELL+i)%2)) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc; - else - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x808080); - } - } - else if (wt == WL_EHOLE) - { - if (powered) - { - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x242424); - for (int j = 0; j < CELL; j += 2) - for (int i = 0; i < CELL; i += 2) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x000000); - } - else - { - for (int j = 0; j < CELL; j += 2) - for (int i =0; i < CELL; i += 2) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x242424); - } - } - else if (wt == WL_STREAM) - { - float xf = x*CELL + CELL*0.5f; - float yf = y*CELL + CELL*0.5f; - int oldX = (int)(xf+0.5f), oldY = (int)(yf+0.5f); - int newX, newY; - float xVel = sim->vx[y][x]*0.125f, yVel = sim->vy[y][x]*0.125f; - // there is no velocity here, draw a streamline and continue - if (!xVel && !yVel) - { - drawtext(x*CELL, y*CELL-2, 0xE00D, 255, 255, 255, 128); - addpixel(oldX, oldY, 255, 255, 255, 255); - continue; - } - bool changed = false; - for (int t = 0; t < 1024; t++) - { - newX = (int)(xf+0.5f); - newY = (int)(yf+0.5f); - if (newX != oldX || newY != oldY) - { - changed = true; - oldX = newX; - oldY = newY; - } - if (changed && (newX<0 || newX>=XRES || newY<0 || newY>=YRES)) - break; - addpixel(newX, newY, 255, 255, 255, 64); - // cache velocity and other checks so we aren't running them constantly - if (changed) - { - int wallX = newX/CELL; - int wallY = newY/CELL; - xVel = sim->vx[wallY][wallX]*0.125f; - yVel = sim->vy[wallY][wallX]*0.125f; - if (wallX != x && wallY != y && sim->bmap[wallY][wallX] == WL_STREAM) - break; - } - xf += xVel; - yf += yVel; - } - drawtext(x*CELL, y*CELL-2, 0xE00D, 255, 255, 255, 128); - } - break; - case 1: - for (int j = 0; j < CELL; j += 2) - for (int i = (j>>1)&1; i < CELL; i += 2) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc; - break; - case 2: - for (int j = 0; j < CELL; j += 2) - for (int i = 0; i < CELL; i += 2) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc; - break; - case 3: - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc; - break; - case 4: - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - if (i == j) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = pc; - else if (i == j+1 || (i == 0 && j == CELL-1)) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = gc; - else - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x202020); - break; - } - - // when in blob view, draw some blobs... - if (render_mode & PMODE_BLOB) - { - switch (sim->wtypes[wt].drawstyle) - { - case 0: - if (wt == WL_EWALL || wt == WL_STASIS) - { - bool reverse = wt == WL_STASIS; - if ((powered>0) ^ reverse) - { - for (int j = 0; j < CELL; j++) - for (int i =0; i < CELL; i++) - if (i&j&1) - drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); - } - else - { - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - if (!(i&j&1)) - drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); - } - } - else if (wt == WL_WALLELEC) - { - for (int j = 0; j < CELL; j++) - for (int i =0; i < CELL; i++) - { - if (!((y*CELL+j)%2) && !((x*CELL+i)%2)) - drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); - else - drawblob((x*CELL+i), (y*CELL+j), 0x80, 0x80, 0x80); - } - } - else if (wt == WL_EHOLE) - { - if (powered) - { - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - drawblob((x*CELL+i), (y*CELL+j), 0x24, 0x24, 0x24); - for (int j = 0; j < CELL; j += 2) - for (int i = 0; i < CELL; i += 2) - // looks bad if drawing black blobs - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x000000); - } - else - { - for (int j = 0; j < CELL; j += 2) - for (int i = 0; i < CELL; i += 2) - drawblob((x*CELL+i), (y*CELL+j), 0x24, 0x24, 0x24); - } - } - break; - case 1: - for (int j = 0; j < CELL; j += 2) - for (int i = (j>>1)&1; i < CELL; i += 2) - drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); - break; - case 2: - for (int j = 0; j < CELL; j += 2) - for (int i = 0; i < CELL; i+=2) - drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); - break; - case 3: - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); - break; - case 4: - for (int j = 0; j < CELL; j++) - for (int i = 0; i < CELL; i++) - if (i == j) - drawblob((x*CELL+i), (y*CELL+j), PIXR(pc), PIXG(pc), PIXB(pc)); - else if (i == j+1 || (i == 0 && j == CELL-1)) - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = gc; - else - // looks bad if drawing black blobs - vid[(y*CELL+j)*(VIDXRES)+(x*CELL+i)] = PIXPACK(0x202020); - break; - } - } - - if (sim->wtypes[wt].eglow && powered) - { - // glow if electrified - pixel glow = sim->wtypes[wt].eglow; - int alpha = 255; - int cr = (alpha*PIXR(glow) + (255-alpha)*fire_r[y/CELL][x/CELL]) >> 8; - int cg = (alpha*PIXG(glow) + (255-alpha)*fire_g[y/CELL][x/CELL]) >> 8; - int cb = (alpha*PIXB(glow) + (255-alpha)*fire_b[y/CELL][x/CELL]) >> 8; - - if (cr > 255) - cr = 255; - if (cg > 255) - cg = 255; - if (cb > 255) - cb = 255; - fire_r[y][x] = cr; - fire_g[y][x] = cg; - fire_b[y][x] = cb; - } - } -} - -#ifndef FONTEDITOR void Renderer::DrawSigns() { int x, y, w, h; @@ -575,9 +147,9 @@ void Renderer::DrawSigns() if (currentSign.text.length()) { String text = currentSign.getDisplayText(sim, x, y, w, h); - clearrect(x, y, w+1, h); - drawrect(x, y, w+1, h, 192, 192, 192, 255); - drawtext(x+3, y+4, text, 255, 255, 255, 255); + DrawFilledRect(RectSized(Vec2{ x + 1, y + 1 }, Vec2{ w, h - 1 }), 0x000000_rgb); + DrawRect(RectSized(Vec2{ x, y }, Vec2{ w+1, h }), 0xC0C0C0_rgb); + BlendText({ x+3, y+4 }, text, 0xFFFFFF_rgb .WithAlpha(255)); if (currentSign.ju != sign::None) { @@ -587,7 +159,7 @@ void Renderer::DrawSigns() int dy = (currentSign.y > 18) ? -1 : 1; for (int j = 0; j < 4; j++) { - blendpixel(x, y, 192, 192, 192, 255); + DrawPixel({ x, y }, 0xC0C0C0_rgb); x += dx; y += dy; } @@ -595,109 +167,7 @@ void Renderer::DrawSigns() } } } -#endif -void Renderer::render_gravlensing(pixel * source) -{ - int nx, ny, rx, ry, gx, gy, bx, by, co; - int r, g, b; - pixel t; - pixel *src = source; - pixel *dst = vid; - if (!dst) - return; - for(nx = 0; nx < XRES; nx++) - { - for(ny = 0; ny < YRES; ny++) - { - co = (ny/CELL)*(XRES/CELL)+(nx/CELL); - rx = (int)(nx-sim->gravx[co]*0.75f+0.5f); - ry = (int)(ny-sim->gravy[co]*0.75f+0.5f); - gx = (int)(nx-sim->gravx[co]*0.875f+0.5f); - gy = (int)(ny-sim->gravy[co]*0.875f+0.5f); - bx = (int)(nx-sim->gravx[co]+0.5f); - by = (int)(ny-sim->gravy[co]+0.5f); - if(rx >= 0 && rx < XRES && ry >= 0 && ry < YRES && gx >= 0 && gx < XRES && gy >= 0 && gy < YRES && bx >= 0 && bx < XRES && by >= 0 && by < YRES) - { - t = dst[ny*(VIDXRES)+nx]; - r = PIXR(src[ry*(VIDXRES)+rx]) + PIXR(t); - g = PIXG(src[gy*(VIDXRES)+gx]) + PIXG(t); - b = PIXB(src[by*(VIDXRES)+bx]) + PIXB(t); - if (r>255) - r = 255; - if (g>255) - g = 255; - if (b>255) - b = 255; - dst[ny*(VIDXRES)+nx] = PIXRGB(r,g,b); - } - } - } -} - -void Renderer::render_fire() -{ - if(!(render_mode & FIREMODE)) - return; - int i,j,x,y,r,g,b,a; - for (j=0; j=0 && j+y>=0 && i+x4 ? r-4 : 0; - fire_g[j][i] = g>4 ? g-4 : 0; - fire_b[j][i] = b>4 ? b-4 : 0; - } -} - -float temp[CELL*3][CELL*3]; -float fire_alphaf[CELL*3][CELL*3]; -float glow_alphaf[11][11]; -float blur_alphaf[7][7]; -void Renderer::prepare_alpha(int size, float intensity) -{ - //TODO: implement size - int x,y,i,j; - float multiplier = 255.0f*intensity; - - memset(temp, 0, sizeof(temp)); - for (x=0; x colour = elements[t].Colour; + colr = colour.Red; + colg = colour.Green; + colb = colour.Blue; firer = fireg = fireb = firea = 0; deca = (sim->parts[i].dcolour>>24)&0xFF; @@ -791,8 +262,8 @@ void Renderer::render_parts() gradv = 3.1415/(2*elements[t].HighTemperature-(elements[t].HighTemperature-800.0f)); auto caddress = int((sim->parts[i].temp>elements[t].HighTemperature)?elements[t].HighTemperature-(elements[t].HighTemperature-800.0f):sim->parts[i].temp-(elements[t].HighTemperature-800.0f)); colr += int(sin(gradv*caddress) * 226); - colg += int(sin(gradv*caddress*4.55 +3.14) * 34); - colb += int(sin(gradv*caddress*2.22 +3.14) * 64); + colg += int(sin(gradv*caddress*4.55 +TPT_PI_DBL) * 34); + colb += int(sin(gradv*caddress*2.22 +TPT_PI_DBL) * 64); } if((pixel_mode & FIRE_ADD) && !(render_mode & FIRE_ADD)) @@ -814,10 +285,10 @@ void Renderer::render_parts() constexpr float min_temp = MIN_TEMP; constexpr float max_temp = MAX_TEMP; firea = 255; - auto color = heatTableAt(int((sim->parts[i].temp - min_temp) / (max_temp - min_temp) * 1024)); - firer = colr = PIXR(color); - fireg = colg = PIXG(color); - fireb = colb = PIXB(color); + RGB color = heatTableAt(int((sim->parts[i].temp - min_temp) / (max_temp - min_temp) * 1024)); + firer = colr = color.Red; + fireg = colg = color.Green; + fireb = colb = color.Blue; cola = 255; if(pixel_mode & (FIREMODE | PMODE_GLOW)) pixel_mode = (pixel_mode & ~(FIREMODE|PMODE_GLOW)) | PMODE_BLUR; @@ -844,9 +315,9 @@ void Renderer::render_parts() } else if(colour_mode & COLOUR_BASC) { - colr = PIXR(elements[t].Colour); - colg = PIXG(elements[t].Colour); - colb = PIXB(elements[t].Colour); + colr = colour.Red; + colg = colour.Green; + colb = colour.Blue; pixel_mode = PMODE_FLAT; } @@ -924,7 +395,7 @@ void Renderer::render_parts() if (t==PT_SOAP) { if ((parts[i].ctype&3) == 3 && parts[i].tmp >= 0 && parts[i].tmp < NPART) - draw_line(nx, ny, (int)(parts[parts[i].tmp].x+0.5f), (int)(parts[parts[i].tmp].y+0.5f), colr, colg, colb, cola); + BlendLine({ nx, ny }, { int(parts[parts[i].tmp].x+0.5f), int(parts[parts[i].tmp].y+0.5f) }, RGBA(colr, colg, colb, cola)); } } if(pixel_mode & PSPEC_STICKMAN) @@ -943,7 +414,7 @@ void Renderer::render_parts() if (mousePos.X>(nx-3) && mousePos.X<(nx+3) && mousePos.Y<(ny+3) && mousePos.Y>(ny-3)) //If mouse is in the head { String hp = String::Build(Format::Width(sim->parts[i].life, 3)); - drawtext(mousePos.X-8-2*(sim->parts[i].life<100)-2*(sim->parts[i].life<10), mousePos.Y-12, hp, 255, 255, 255, 255); + BlendText(mousePos + Vec2{ -8-2*(sim->parts[i].life<100)-2*(sim->parts[i].life<10), -12 }, hp, 0xFFFFFF_rgb .WithAlpha(255)); } if (findingElement == t) @@ -955,15 +426,17 @@ void Renderer::render_parts() { if (cplayer->fan) { - colr = PIXR(0x8080FF); - colg = PIXG(0x8080FF); - colb = PIXB(0x8080FF); + auto fanColor = 0x8080FF_rgb; + colr = fanColor.Red; + colg = fanColor.Green; + colb = fanColor.Blue; } else if (cplayer->elem < PT_NUM && cplayer->elem > 0) { - colr = PIXR(elements[cplayer->elem].Colour); - colg = PIXG(elements[cplayer->elem].Colour); - colb = PIXB(elements[cplayer->elem].Colour); + RGB elemColour = elements[cplayer->elem].Colour; + colr = elemColour.Red; + colg = elemColour.Green; + colb = elemColour.Blue; } else { @@ -1010,23 +483,23 @@ void Renderer::render_parts() //head if(t==PT_FIGH) { - draw_line(nx, ny+2, nx+2, ny, colr, colg, colb, 255); - draw_line(nx+2, ny, nx, ny-2, colr, colg, colb, 255); - draw_line(nx, ny-2, nx-2, ny, colr, colg, colb, 255); - draw_line(nx-2, ny, nx, ny+2, colr, colg, colb, 255); + DrawLine({ nx, ny+2 }, { nx+2, ny }, RGB(colr, colg, colb)); + DrawLine({ nx+2, ny }, { nx, ny-2 }, RGB(colr, colg, colb)); + DrawLine({ nx, ny-2 }, { nx-2, ny }, RGB(colr, colg, colb)); + DrawLine({ nx-2, ny }, { nx, ny+2 }, RGB(colr, colg, colb)); } else { - draw_line(nx-2, ny+2, nx+2, ny+2, colr, colg, colb, 255); - draw_line(nx-2, ny-2, nx+2, ny-2, colr, colg, colb, 255); - draw_line(nx-2, ny-2, nx-2, ny+2, colr, colg, colb, 255); - draw_line(nx+2, ny-2, nx+2, ny+2, colr, colg, colb, 255); + DrawLine({ nx-2, ny+2 }, { nx+2, ny+2 }, RGB(colr, colg, colb)); + DrawLine({ nx-2, ny-2 }, { nx+2, ny-2 }, RGB(colr, colg, colb)); + DrawLine({ nx-2, ny-2 }, { nx-2, ny+2 }, RGB(colr, colg, colb)); + DrawLine({ nx+2, ny-2 }, { nx+2, ny+2 }, RGB(colr, colg, colb)); } //legs - draw_line(nx, ny+3, int(cplayer->legs[0]), int(cplayer->legs[1]), legr, legg, legb, 255); - draw_line(int(cplayer->legs[0]), int(cplayer->legs[1]), int(cplayer->legs[4]), int(cplayer->legs[5]), legr, legg, legb, 255); - draw_line(nx, ny+3, int(cplayer->legs[8]), int(cplayer->legs[9]), legr, legg, legb, 255); - draw_line(int(cplayer->legs[8]), int(cplayer->legs[9]), int(cplayer->legs[12]), int(cplayer->legs[13]), legr, legg, legb, 255); + DrawLine({ nx, ny+3 }, { int(cplayer->legs[ 0]), int(cplayer->legs[ 1]) }, RGB(legr, legg, legb)); + DrawLine({ int(cplayer->legs[0]), int(cplayer->legs[1]) }, { int(cplayer->legs[ 4]), int(cplayer->legs[ 5]) }, RGB(legr, legg, legb)); + DrawLine({ nx, ny+3 }, { int(cplayer->legs[ 8]), int(cplayer->legs[ 9]) }, RGB(legr, legg, legb)); + DrawLine({ int(cplayer->legs[8]), int(cplayer->legs[9]) }, { int(cplayer->legs[12]), int(cplayer->legs[13]) }, RGB(legr, legg, legb)); if (cplayer->rocketBoots) { for (int leg=0; leg<2; leg++) @@ -1034,68 +507,68 @@ void Renderer::render_parts() int nx = int(cplayer->legs[leg*8+4]), ny = int(cplayer->legs[leg*8+5]); int colr = 255, colg = 0, colb = 255; if (((int)(cplayer->comm)&0x04) == 0x04 || (((int)(cplayer->comm)&0x01) == 0x01 && leg==0) || (((int)(cplayer->comm)&0x02) == 0x02 && leg==1)) - blendpixel(nx, ny, 0, 255, 0, 255); + DrawPixel({ nx, ny }, 0x00FF00_rgb); else - blendpixel(nx, ny, 255, 0, 0, 255); - blendpixel(nx+1, ny, colr, colg, colb, 223); - blendpixel(nx-1, ny, colr, colg, colb, 223); - blendpixel(nx, ny+1, colr, colg, colb, 223); - blendpixel(nx, ny-1, colr, colg, colb, 223); + DrawPixel({ nx, ny }, 0xFF0000_rgb); + BlendPixel({ nx+1, ny }, RGBA(colr, colg, colb, 223)); + BlendPixel({ nx-1, ny }, RGBA(colr, colg, colb, 223)); + BlendPixel({ nx, ny+1 }, RGBA(colr, colg, colb, 223)); + BlendPixel({ nx, ny-1 }, RGBA(colr, colg, colb, 223)); - blendpixel(nx+1, ny-1, colr, colg, colb, 112); - blendpixel(nx-1, ny-1, colr, colg, colb, 112); - blendpixel(nx+1, ny+1, colr, colg, colb, 112); - blendpixel(nx-1, ny+1, colr, colg, colb, 112); + BlendPixel({ nx+1, ny-1 }, RGBA(colr, colg, colb, 112)); + BlendPixel({ nx-1, ny-1 }, RGBA(colr, colg, colb, 112)); + BlendPixel({ nx+1, ny+1 }, RGBA(colr, colg, colb, 112)); + BlendPixel({ nx-1, ny+1 }, RGBA(colr, colg, colb, 112)); } } } if(pixel_mode & PMODE_FLAT) { - vid[ny*(VIDXRES)+nx] = PIXRGB(colr,colg,colb); + video[{ nx, ny }] = RGB(colr, colg, colb).Pack(); } if(pixel_mode & PMODE_BLEND) { - blendpixel(nx, ny, colr, colg, colb, cola); + BlendPixel({ nx, ny }, RGBA(colr, colg, colb, cola)); } if(pixel_mode & PMODE_ADD) { - addpixel(nx, ny, colr, colg, colb, cola); + AddPixel({ nx, ny }, RGBA(colr, colg, colb, cola)); } if(pixel_mode & PMODE_BLOB) { - vid[ny*(VIDXRES)+nx] = PIXRGB(colr,colg,colb); + video[{ nx, ny }] = RGB(colr, colg, colb).Pack(); - blendpixel(nx+1, ny, colr, colg, colb, 223); - blendpixel(nx-1, ny, colr, colg, colb, 223); - blendpixel(nx, ny+1, colr, colg, colb, 223); - blendpixel(nx, ny-1, colr, colg, colb, 223); + BlendPixel({ nx+1, ny }, RGBA(colr, colg, colb, 223)); + BlendPixel({ nx-1, ny }, RGBA(colr, colg, colb, 223)); + BlendPixel({ nx, ny+1 }, RGBA(colr, colg, colb, 223)); + BlendPixel({ nx, ny-1 }, RGBA(colr, colg, colb, 223)); - blendpixel(nx+1, ny-1, colr, colg, colb, 112); - blendpixel(nx-1, ny-1, colr, colg, colb, 112); - blendpixel(nx+1, ny+1, colr, colg, colb, 112); - blendpixel(nx-1, ny+1, colr, colg, colb, 112); + BlendPixel({ nx+1, ny-1 }, RGBA(colr, colg, colb, 112)); + BlendPixel({ nx-1, ny-1 }, RGBA(colr, colg, colb, 112)); + BlendPixel({ nx+1, ny+1 }, RGBA(colr, colg, colb, 112)); + BlendPixel({ nx-1, ny+1 }, RGBA(colr, colg, colb, 112)); } if(pixel_mode & PMODE_GLOW) { int cola1 = (5*cola)/255; - addpixel(nx, ny, colr, colg, colb, (192*cola)/255); - addpixel(nx+1, ny, colr, colg, colb, (96*cola)/255); - addpixel(nx-1, ny, colr, colg, colb, (96*cola)/255); - addpixel(nx, ny+1, colr, colg, colb, (96*cola)/255); - addpixel(nx, ny-1, colr, colg, colb, (96*cola)/255); + AddPixel({ nx, ny }, RGBA(colr, colg, colb, (192*cola)/255)); + AddPixel({ nx+1, ny }, RGBA(colr, colg, colb, (96*cola)/255)); + AddPixel({ nx-1, ny }, RGBA(colr, colg, colb, (96*cola)/255)); + AddPixel({ nx, ny+1 }, RGBA(colr, colg, colb, (96*cola)/255)); + AddPixel({ nx, ny-1 }, RGBA(colr, colg, colb, (96*cola)/255)); for (x = 1; x < 6; x++) { - addpixel(nx, ny-x, colr, colg, colb, cola1); - addpixel(nx, ny+x, colr, colg, colb, cola1); - addpixel(nx-x, ny, colr, colg, colb, cola1); - addpixel(nx+x, ny, colr, colg, colb, cola1); + AddPixel({ nx, ny-x }, RGBA(colr, colg, colb, cola1)); + AddPixel({ nx, ny+x }, RGBA(colr, colg, colb, cola1)); + AddPixel({ nx-x, ny }, RGBA(colr, colg, colb, cola1)); + AddPixel({ nx+x, ny }, RGBA(colr, colg, colb, cola1)); for (y = 1; y < 6; y++) { if(x + y > 7) continue; - addpixel(nx+x, ny-y, colr, colg, colb, cola1); - addpixel(nx-x, ny+y, colr, colg, colb, cola1); - addpixel(nx+x, ny+y, colr, colg, colb, cola1); - addpixel(nx-x, ny-y, colr, colg, colb, cola1); + AddPixel({ nx+x, ny-y }, RGBA(colr, colg, colb, cola1)); + AddPixel({ nx-x, ny+y }, RGBA(colr, colg, colb, cola1)); + AddPixel({ nx+x, ny+y }, RGBA(colr, colg, colb, cola1)); + AddPixel({ nx-x, ny-y }, RGBA(colr, colg, colb, cola1)); } } } @@ -1106,68 +579,67 @@ void Renderer::render_parts() for (y=-3; y<4; y++) { if (abs(x)+abs(y) <2 && !(abs(x)==2||abs(y)==2)) - blendpixel(x+nx, y+ny, colr, colg, colb, 30); + BlendPixel({ x+nx, y+ny }, RGBA(colr, colg, colb, 30)); if (abs(x)+abs(y) <=3 && abs(x)+abs(y)) - blendpixel(x+nx, y+ny, colr, colg, colb, 20); + BlendPixel({ x+nx, y+ny }, RGBA(colr, colg, colb, 20)); if (abs(x)+abs(y) == 2) - blendpixel(x+nx, y+ny, colr, colg, colb, 10); + BlendPixel({ x+nx, y+ny }, RGBA(colr, colg, colb, 10)); } } } if(pixel_mode & PMODE_SPARK) { - flicker = float(random_gen()%20); + flicker = float(rng()%20); gradv = 4*sim->parts[i].life + flicker; for (x = 0; gradv>0.5; x++) { - addpixel(nx+x, ny, colr, colg, colb, int(gradv)); - addpixel(nx-x, ny, colr, colg, colb, int(gradv)); - - addpixel(nx, ny+x, colr, colg, colb, int(gradv)); - addpixel(nx, ny-x, colr, colg, colb, int(gradv)); + AddPixel({ nx+x, ny }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx-x, ny }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx, ny+x }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx, ny-x }, RGBA(colr, colg, colb, int(gradv))); gradv = gradv/1.5f; } } if(pixel_mode & PMODE_FLARE) { - flicker = float(random_gen()%20); + flicker = float(rng()%20); gradv = flicker + fabs(parts[i].vx)*17 + fabs(sim->parts[i].vy)*17; - blendpixel(nx, ny, colr, colg, colb, int((gradv*4)>255?255:(gradv*4)) ); - blendpixel(nx+1, ny, colr, colg, colb,int( (gradv*2)>255?255:(gradv*2)) ); - blendpixel(nx-1, ny, colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) ); - blendpixel(nx, ny+1, colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) ); - blendpixel(nx, ny-1, colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) ); + BlendPixel({ nx, ny }, RGBA(colr, colg, colb, int((gradv*4)>255?255:(gradv*4)) )); + BlendPixel({ nx+1, ny }, RGBA(colr, colg, colb,int( (gradv*2)>255?255:(gradv*2)) )); + BlendPixel({ nx-1, ny }, RGBA(colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) )); + BlendPixel({ nx, ny+1 }, RGBA(colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) )); + BlendPixel({ nx, ny-1 }, RGBA(colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) )); if (gradv>255) gradv=255; - blendpixel(nx+1, ny-1, colr, colg, colb, int(gradv)); - blendpixel(nx-1, ny-1, colr, colg, colb, int(gradv)); - blendpixel(nx+1, ny+1, colr, colg, colb, int(gradv)); - blendpixel(nx-1, ny+1, colr, colg, colb, int(gradv)); + BlendPixel({ nx+1, ny-1 }, RGBA(colr, colg, colb, int(gradv))); + BlendPixel({ nx-1, ny-1 }, RGBA(colr, colg, colb, int(gradv))); + BlendPixel({ nx+1, ny+1 }, RGBA(colr, colg, colb, int(gradv))); + BlendPixel({ nx-1, ny+1 }, RGBA(colr, colg, colb, int(gradv))); for (x = 1; gradv>0.5; x++) { - addpixel(nx+x, ny, colr, colg, colb, int(gradv)); - addpixel(nx-x, ny, colr, colg, colb, int(gradv)); - addpixel(nx, ny+x, colr, colg, colb, int(gradv)); - addpixel(nx, ny-x, colr, colg, colb, int(gradv)); + AddPixel({ nx+x, ny }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx-x, ny }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx, ny+x }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx, ny-x }, RGBA(colr, colg, colb, int(gradv))); gradv = gradv/1.2f; } } if(pixel_mode & PMODE_LFLARE) { - flicker = float(random_gen()%20); + flicker = float(rng()%20); gradv = flicker + fabs(parts[i].vx)*17 + fabs(parts[i].vy)*17; - blendpixel(nx, ny, colr, colg, colb, int((gradv*4)>255?255:(gradv*4)) ); - blendpixel(nx+1, ny, colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) ); - blendpixel(nx-1, ny, colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) ); - blendpixel(nx, ny+1, colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) ); - blendpixel(nx, ny-1, colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) ); + BlendPixel({ nx, ny }, RGBA(colr, colg, colb, int((gradv*4)>255?255:(gradv*4)) )); + BlendPixel({ nx+1, ny }, RGBA(colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) )); + BlendPixel({ nx-1, ny }, RGBA(colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) )); + BlendPixel({ nx, ny+1 }, RGBA(colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) )); + BlendPixel({ nx, ny-1 }, RGBA(colr, colg, colb, int((gradv*2)>255?255:(gradv*2)) )); if (gradv>255) gradv=255; - blendpixel(nx+1, ny-1, colr, colg, colb, int(gradv)); - blendpixel(nx-1, ny-1, colr, colg, colb, int(gradv)); - blendpixel(nx+1, ny+1, colr, colg, colb, int(gradv)); - blendpixel(nx-1, ny+1, colr, colg, colb, int(gradv)); + BlendPixel({ nx+1, ny-1 }, RGBA(colr, colg, colb, int(gradv))); + BlendPixel({ nx-1, ny-1 }, RGBA(colr, colg, colb, int(gradv))); + BlendPixel({ nx+1, ny+1 }, RGBA(colr, colg, colb, int(gradv))); + BlendPixel({ nx-1, ny+1 }, RGBA(colr, colg, colb, int(gradv))); for (x = 1; gradv>0.5; x++) { - addpixel(nx+x, ny, colr, colg, colb, int(gradv)); - addpixel(nx-x, ny, colr, colg, colb, int(gradv)); - addpixel(nx, ny+x, colr, colg, colb, int(gradv)); - addpixel(nx, ny-x, colr, colg, colb, int(gradv)); + AddPixel({ nx+x, ny }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx-x, ny }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx, ny+x }, RGBA(colr, colg, colb, int(gradv))); + AddPixel({ nx, ny-x }, RGBA(colr, colg, colb, int(gradv))); gradv = gradv/1.01f; } } @@ -1181,11 +653,11 @@ void Renderer::render_parts() sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl); for (r = 0; r < 4; r++) { ddist = ((float)orbd[r])/16.0f; - drad = (M_PI * ((float)orbl[r]) / 180.0f)*1.41f; + drad = (TPT_PI_FLT * ((float)orbl[r]) / 180.0f)*1.41f; nxo = (int)(ddist*cos(drad)); nyo = (int)(ddist*sin(drad)); if (ny+nyo>0 && ny+nyo0 && nx+nxopmap[ny+nyo][nx+nxo]) != PT_PRTI) - addpixel(nx+nxo, ny+nyo, colr, colg, colb, 255-orbd[r]); + AddPixel({ nx+nxo, ny+nyo }, RGBA(colr, colg, colb, 255-orbd[r])); } } if (pixel_mode & EFFECT_GRAVOUT) @@ -1198,11 +670,11 @@ void Renderer::render_parts() sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl); for (r = 0; r < 4; r++) { ddist = ((float)orbd[r])/16.0f; - drad = (M_PI * ((float)orbl[r]) / 180.0f)*1.41f; + drad = (TPT_PI_FLT * ((float)orbl[r]) / 180.0f)*1.41f; nxo = (int)(ddist*cos(drad)); nyo = (int)(ddist*sin(drad)); if (ny+nyo>0 && ny+nyo0 && nx+nxopmap[ny+nyo][nx+nxo]) != PT_PRTO) - addpixel(nx+nxo, ny+nyo, colr, colg, colb, 255-orbd[r]); + AddPixel({ nx+nxo, ny+nyo }, RGBA(colr, colg, colb, 255-orbd[r])); } } if (pixel_mode & EFFECT_DBGLINES && !(display_mode&DISPLAY_PERS)) @@ -1221,7 +693,7 @@ void Renderer::render_parts() { othertmp = (int)((parts[z].temp-73.15f)/100+1); if (tmp == othertmp) - xor_line(nx,ny,(int)(parts[z].x+0.5f),(int)(parts[z].y+0.5f)); + XorLine({ nx, ny }, Vec2{ int(parts[z].x+0.5f), int(parts[z].y+0.5f) }); } } } @@ -1283,11 +755,33 @@ void Renderer::draw_other() // EMP effect for (j=0; j(r, g, b, a)); } } } -#endif + +void Renderer::draw_grav_zones() +{ + if(!gravityZonesEnabled) + return; + + int x, y, i, j; + for (y=0; ygrav->gravmask[y*XCELLS+x]) + { + for (j=0; jgravx[ca]) <= 0.001f && fabsf(sim->gravy[ca]) <= 0.001f) continue; nx = float(x*CELL); @@ -1311,20 +805,12 @@ void Renderer::draw_grav() { nx -= sim->gravx[ca]*0.5f; ny -= sim->gravy[ca]*0.5f; - addpixel((int)(nx+0.5f), (int)(ny+0.5f), 255, 255, 255, (int)(dist*20.0f)); + AddPixel({ int(nx+0.5f), int(ny+0.5f) }, 0xFFFFFF_rgb .WithAlpha(int(dist*20.0f))); } } } } -int HeatToColour(float temp) -{ - constexpr float min_temp = MIN_TEMP; - constexpr float max_temp = MAX_TEMP; - auto color = Renderer::heatTableAt(int((temp - min_temp) / (max_temp - min_temp) * 1024)); - return PIXRGB((int)(PIXR(color)*0.7f), (int)(PIXG(color)*0.7f), (int)(PIXB(color)*0.7f)); -} - void Renderer::draw_air() { if(!sim->aheat_enable && (display_mode & DISPLAY_AIRH)) @@ -1332,33 +818,33 @@ void Renderer::draw_air() if(!(display_mode & DISPLAY_AIR)) return; int x, y, i, j; - float (*pv)[XRES/CELL] = sim->air->pv; - float (*hv)[XRES/CELL] = sim->air->hv; - float (*vx)[XRES/CELL] = sim->air->vx; - float (*vy)[XRES/CELL] = sim->air->vy; - pixel c = 0; - for (y=0; yair->pv; + float (*hv)[XCELLS] = sim->air->hv; + float (*vx)[XCELLS] = sim->air->vx; + float (*vy)[XCELLS] = sim->air->vy; + auto c = 0x000000_rgb; + for (y=0; y 0.0f) - c = PIXRGB(clamp_flt(pv[y][x], 0.0f, 8.0f), 0, 0);//positive pressure is red! + c = RGB(clamp_flt(pv[y][x], 0.0f, 8.0f), 0, 0);//positive pressure is red! else - c = PIXRGB(0, 0, clamp_flt(-pv[y][x], 0.0f, 8.0f));//negative pressure is blue! + c = RGB(0, 0, clamp_flt(-pv[y][x], 0.0f, 8.0f));//negative pressure is blue! } else if (display_mode & DISPLAY_AIRV) { - c = PIXRGB(clamp_flt(fabsf(vx[y][x]), 0.0f, 8.0f),//vx adds red + c = RGB(clamp_flt(fabsf(vx[y][x]), 0.0f, 8.0f),//vx adds red clamp_flt(pv[y][x], 0.0f, 8.0f),//pressure adds green clamp_flt(fabsf(vy[y][x]), 0.0f, 8.0f));//vy adds blue } else if (display_mode & DISPLAY_AIRH) { - c = HeatToColour(hv[y][x]); - //c = PIXRGB(clamp_flt(fabsf(vx[y][x]), 0.0f, 8.0f),//vx adds red + c = RGB::Unpack(HeatToColour(hv[y][x])); + //c = RGB(clamp_flt(fabsf(vx[y][x]), 0.0f, 8.0f),//vx adds red // clamp_flt(hv[y][x], 0.0f, 1600.0f),//heat adds green - // clamp_flt(fabsf(vy[y][x]), 0.0f, 8.0f));//vy adds blue + // clamp_flt(fabsf(vy[y][x]), 0.0f, 8.0f)).Pack();//vy adds blue } else if (display_mode & DISPLAY_AIRC) { @@ -1378,7 +864,7 @@ void Renderer::draw_air() g=255; if (b>255) b=255; - c = PIXRGB(r, g, b); + c = RGB(r, g, b); } else { @@ -1389,379 +875,321 @@ void Renderer::draw_air() g=255; if (b>255) b=255; - c = PIXRGB(r, g, b); + c = RGB(r, g, b); } } if (findingElement) - c = PIXRGB(PIXR(c)/10,PIXG(c)/10,PIXB(c)/10); + { + c.Red /= 10; + c.Green /= 10; + c.Blue /= 10; + } for (j=0; jgrav->gravmask[y*(XRES/CELL)+x]) + for (int y = 0; y < YCELLS; y++) + for (int x =0; x < XCELLS; x++) + if (sim->bmap[y][x]) { - for (j=0; jbmap[y][x]; + if (wt >= UI_WALLCOUNT) + continue; + unsigned char powered = sim->emap[y][x]; + RGB prgb = sim->wtypes[wt].colour; + RGB grgb = sim->wtypes[wt].eglow; + + if (findingElement) + { + prgb.Red /= 10; + prgb.Green /= 10; + prgb.Blue /= 10; + grgb.Red /= 10; + grgb.Green /= 10; + grgb.Blue /= 10; + } + + pixel pc = prgb.Pack(); + pixel gc = grgb.Pack(); + + switch (sim->wtypes[wt].drawstyle) + { + case 0: + if (wt == WL_EWALL || wt == WL_STASIS) + { + bool reverse = wt == WL_STASIS; + if ((powered > 0) ^ reverse) + { + for (int j = 0; j < CELL; j++) + for (int i =0; i < CELL; i++) + if (i&j&1) + video[{ x * CELL + i, y * CELL + j }] = pc; + } else - blendpixel(x*CELL+i, y*CELL+j, 32, 32, 32, 120); + { + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + if (!(i&j&1)) + video[{ x * CELL + i, y * CELL + j }] = pc; + } + } + else if (wt == WL_WALLELEC) + { + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + { + if (!((y*CELL+j)%2) && !((x*CELL+i)%2)) + video[{ x * CELL + i, y * CELL + j }] = pc; + else + video[{ x * CELL + i, y * CELL + j }] = 0x808080_rgb .Pack(); + } + } + else if (wt == WL_EHOLE) + { + if (powered) + { + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + video[{ x * CELL + i, y * CELL + j }] = 0x242424_rgb .Pack(); + for (int j = 0; j < CELL; j += 2) + for (int i = 0; i < CELL; i += 2) + video[{ x * CELL + i, y * CELL + j }] = 0x000000_rgb .Pack(); + } + else + { + for (int j = 0; j < CELL; j += 2) + for (int i =0; i < CELL; i += 2) + video[{ x * CELL + i, y * CELL + j }] = 0x242424_rgb .Pack(); + } + } + else if (wt == WL_STREAM) + { + float xf = x*CELL + CELL*0.5f; + float yf = y*CELL + CELL*0.5f; + int oldX = (int)(xf+0.5f), oldY = (int)(yf+0.5f); + int newX, newY; + float xVel = sim->vx[y][x]*0.125f, yVel = sim->vy[y][x]*0.125f; + // there is no velocity here, draw a streamline and continue + if (!xVel && !yVel) + { + BlendText({ x*CELL, y*CELL-2 }, 0xE00D, 0xFFFFFF_rgb .WithAlpha(128)); + AddPixel({ oldX, oldY }, 0xFFFFFF_rgb .WithAlpha(255)); + continue; + } + bool changed = false; + for (int t = 0; t < 1024; t++) + { + newX = (int)(xf+0.5f); + newY = (int)(yf+0.5f); + if (newX != oldX || newY != oldY) + { + changed = true; + oldX = newX; + oldY = newY; + } + if (changed && (newX<0 || newX>=XRES || newY<0 || newY>=YRES)) + break; + AddPixel({ newX, newY }, 0xFFFFFF_rgb .WithAlpha(64)); + // cache velocity and other checks so we aren't running them constantly + if (changed) + { + int wallX = newX/CELL; + int wallY = newY/CELL; + xVel = sim->vx[wallY][wallX]*0.125f; + yVel = sim->vy[wallY][wallX]*0.125f; + if (wallX != x && wallY != y && sim->bmap[wallY][wallX] == WL_STREAM) + break; + } + xf += xVel; + yf += yVel; + } + BlendText({ x*CELL, y*CELL-2 }, 0xE00D, 0xFFFFFF_rgb .WithAlpha(128)); + } + break; + case 1: + for (int j = 0; j < CELL; j += 2) + for (int i = (j>>1)&1; i < CELL; i += 2) + video[{ x * CELL + i, y * CELL + j }] = pc; + break; + case 2: + for (int j = 0; j < CELL; j += 2) + for (int i = 0; i < CELL; i += 2) + video[{ x * CELL + i, y * CELL + j }] = pc; + break; + case 3: + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + video[{ x * CELL + i, y * CELL + j }] = pc; + break; + case 4: + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + if (i == j) + video[{ x * CELL + i, y * CELL + j }] = pc; + else if (i == j+1 || (i == 0 && j == CELL-1)) + video[{ x * CELL + i, y * CELL + j }] = gc; + else + video[{ x * CELL + i, y * CELL + j }] = 0x202020_rgb .Pack(); + break; + } + + // when in blob view, draw some blobs... + if (render_mode & PMODE_BLOB) + { + switch (sim->wtypes[wt].drawstyle) + { + case 0: + if (wt == WL_EWALL || wt == WL_STASIS) + { + bool reverse = wt == WL_STASIS; + if ((powered>0) ^ reverse) + { + for (int j = 0; j < CELL; j++) + for (int i =0; i < CELL; i++) + if (i&j&1) + DrawBlob({ x*CELL+i, y*CELL+j }, prgb); + } + else + { + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + if (!(i&j&1)) + DrawBlob({ x*CELL+i, y*CELL+j }, prgb); + } + } + else if (wt == WL_WALLELEC) + { + for (int j = 0; j < CELL; j++) + for (int i =0; i < CELL; i++) + { + if (!((y*CELL+j)%2) && !((x*CELL+i)%2)) + DrawBlob({ x*CELL+i, y*CELL+j }, prgb); + else + DrawBlob({ x*CELL+i, y*CELL+j }, 0x808080_rgb); + } + } + else if (wt == WL_EHOLE) + { + if (powered) + { + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + DrawBlob({ x*CELL+i, y*CELL+j }, 0x242424_rgb); + for (int j = 0; j < CELL; j += 2) + for (int i = 0; i < CELL; i += 2) + // looks bad if drawing black blobs + video[{ x * CELL + i, y * CELL + j }] = 0x000000_rgb .Pack(); + } + else + { + for (int j = 0; j < CELL; j += 2) + for (int i = 0; i < CELL; i += 2) + DrawBlob({ x*CELL+i, y*CELL+j }, 0x242424_rgb); + } + } + break; + case 1: + for (int j = 0; j < CELL; j += 2) + for (int i = (j>>1)&1; i < CELL; i += 2) + DrawBlob({ x*CELL+i, y*CELL+j }, prgb); + break; + case 2: + for (int j = 0; j < CELL; j += 2) + for (int i = 0; i < CELL; i+=2) + DrawBlob({ x*CELL+i, y*CELL+j }, prgb); + break; + case 3: + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + DrawBlob({ x*CELL+i, y*CELL+j }, prgb); + break; + case 4: + for (int j = 0; j < CELL; j++) + for (int i = 0; i < CELL; i++) + if (i == j) + DrawBlob({ x*CELL+i, y*CELL+j }, prgb); + else if (i == j+1 || (i == 0 && j == CELL-1)) + video[{ x * CELL + i, y * CELL + j }] = gc; + else + // looks bad if drawing black blobs + video[{ x * CELL + i, y * CELL + j }] = 0x202020_rgb .Pack(); + break; + } + } + + if (sim->wtypes[wt].eglow.Pack() && powered) + { + // glow if electrified + RGB glow = sim->wtypes[wt].eglow; + int alpha = 255; + int cr = (alpha*glow.Red + (255-alpha)*fire_r[y/CELL][x/CELL]) >> 8; + int cg = (alpha*glow.Green + (255-alpha)*fire_g[y/CELL][x/CELL]) >> 8; + int cb = (alpha*glow.Blue + (255-alpha)*fire_b[y/CELL][x/CELL]) >> 8; + + if (cr > 255) + cr = 255; + if (cg > 255) + cg = 255; + if (cb > 255) + cb = 255; + fire_r[y][x] = cr; + fire_g[y][x] = cg; + fire_b[y][x] = cb; + } } - } - } } -void Renderer::drawblob(int x, int y, unsigned char cr, unsigned char cg, unsigned char cb) +void Renderer::render_fire() { - blendpixel(x+1, y, cr, cg, cb, 112); - blendpixel(x-1, y, cr, cg, cb, 112); - blendpixel(x, y+1, cr, cg, cb, 112); - blendpixel(x, y-1, cr, cg, cb, 112); - - blendpixel(x+1, y-1, cr, cg, cb, 64); - blendpixel(x-1, y-1, cr, cg, cb, 64); - blendpixel(x+1, y+1, cr, cg, cb, 64); - blendpixel(x-1, y+1, cr, cg, cb, 64); -} - -pixel Renderer::GetPixel(int x, int y) -{ - if (x<0 || y<0 || x>=VIDXRES || y>=VIDYRES) - return 0; - return vid[(y*VIDXRES)+x]; -} - -std::vector Renderer::flameTable; -std::vector Renderer::plasmaTable; -std::vector Renderer::heatTable; -std::vector Renderer::clfmTable; -std::vector Renderer::firwTable; -static bool tablesPopulated = false; -static std::mutex tablesPopulatedMx; -void Renderer::PopulateTables() -{ - std::lock_guard g(tablesPopulatedMx); - if (!tablesPopulated) - { - tablesPopulated = true; - flameTable = Graphics::Gradient({ - { 0x000000, 0.00f }, - { 0x60300F, 0.50f }, - { 0xDFBF6F, 0.90f }, - { 0xAF9F0F, 1.00f }, - }, 200); - plasmaTable = Graphics::Gradient({ - { 0x000000, 0.00f }, - { 0x301040, 0.25f }, - { 0x301060, 0.50f }, - { 0xAFFFFF, 0.90f }, - { 0xAFFFFF, 1.00f }, - }, 200); - heatTable = Graphics::Gradient({ - { 0x2B00FF, 0.00f }, - { 0x003CFF, 0.01f }, - { 0x00C0FF, 0.05f }, - { 0x00FFEB, 0.08f }, - { 0x00FF14, 0.19f }, - { 0x4BFF00, 0.25f }, - { 0xC8FF00, 0.37f }, - { 0xFFDC00, 0.45f }, - { 0xFF0000, 0.71f }, - { 0xFF00DC, 1.00f }, - }, 1024); - clfmTable = Graphics::Gradient({ - { 0x000000, 0.00f }, - { 0x0A0917, 0.10f }, - { 0x19163C, 0.20f }, - { 0x28285E, 0.30f }, - { 0x343E77, 0.40f }, - { 0x49769A, 0.60f }, - { 0x57A0B4, 0.80f }, - { 0x5EC4C6, 1.00f }, - }, 200); - firwTable = Graphics::Gradient({ - { 0xFF00FF, 0.00f }, - { 0x0000FF, 0.20f }, - { 0x00FFFF, 0.40f }, - { 0x00FF00, 0.60f }, - { 0xFFFF00, 0.80f }, - { 0xFF0000, 1.00f }, - }, 200); - } -} - -Renderer::Renderer(Graphics * g, Simulation * sim): - sim(NULL), - g(NULL), - render_mode(0), - colour_mode(0), - display_mode(0), - gravityZonesEnabled(false), - gravityFieldEnabled(false), - decorations_enable(1), - blackDecorations(false), - debugLines(false), - sampleColor(0xFFFFFFFF), - findingElement(0), - foundElements(0), - mousePos(0, 0), - zoomWindowPosition(0, 0), - zoomScopePosition(0, 0), - zoomScopeSize(32), - zoomEnabled(false), - ZFACTOR(8), - gridSize(0) -{ - PopulateTables(); - - this->g = g; - this->sim = sim; - vid = g->vid; - persistentVid = new pixel[VIDXRES*YRES]; - warpVid = new pixel[VIDXRES*VIDYRES]; - - memset(fire_r, 0, sizeof(fire_r)); - memset(fire_g, 0, sizeof(fire_g)); - memset(fire_b, 0, sizeof(fire_b)); - - //Set defauly display modes - ResetModes(); - - //Render mode presets. Possibly load from config in future? - renderModePresets.push_back({ - "Alternative Velocity Display", - { RENDER_EFFE, RENDER_BASC }, - { DISPLAY_AIRC }, - 0 - }); - renderModePresets.push_back({ - "Velocity Display", - { RENDER_EFFE, RENDER_BASC }, - { DISPLAY_AIRV }, - 0 - }); - renderModePresets.push_back({ - "Pressure Display", - { RENDER_EFFE, RENDER_BASC }, - { DISPLAY_AIRP }, - 0 - }); - renderModePresets.push_back({ - "Persistent Display", - { RENDER_EFFE, RENDER_BASC }, - { DISPLAY_PERS }, - 0 - }); - renderModePresets.push_back({ - "Fire Display", - { RENDER_FIRE, RENDER_SPRK, RENDER_EFFE, RENDER_BASC }, - { }, - 0 - }); - renderModePresets.push_back({ - "Blob Display", - { RENDER_FIRE, RENDER_SPRK, RENDER_EFFE, RENDER_BLOB }, - { }, - 0 - }); - renderModePresets.push_back({ - "Heat Display", - { RENDER_BASC }, - { DISPLAY_AIRH }, - COLOUR_HEAT - }); - renderModePresets.push_back({ - "Fancy Display", - { RENDER_FIRE, RENDER_SPRK, RENDER_GLOW, RENDER_BLUR, RENDER_EFFE, RENDER_BASC }, - { DISPLAY_WARP }, - 0 - }); - renderModePresets.push_back({ - "Nothing Display", - { RENDER_BASC }, - { }, - 0 - }); - renderModePresets.push_back({ - "Heat Gradient Display", - { RENDER_BASC }, - { }, - COLOUR_GRAD - }); - renderModePresets.push_back({ - "Life Gradient Display", - { RENDER_BASC }, - { }, - COLOUR_LIFE - }); - - //Prepare the graphics cache - graphicscache = new gcache_item[PT_NUM]; - std::fill(&graphicscache[0], &graphicscache[PT_NUM], gcache_item()); - - prepare_alpha(CELL, 1.0f); -} - -void Renderer::CompileRenderMode() -{ - int old_render_mode = render_mode; - render_mode = 0; - for (size_t i = 0; i < render_modes.size(); i++) - render_mode |= render_modes[i]; - - //If firemode is removed, clear the fire display - if(!(render_mode & FIREMODE) && (old_render_mode & FIREMODE)) - { - ClearAccumulation(); - } -} - -void Renderer::ClearAccumulation() -{ - std::fill(fire_r[0]+0, fire_r[(YRES/CELL)-1]+((XRES/CELL)-1), 0); - std::fill(fire_g[0]+0, fire_g[(YRES/CELL)-1]+((XRES/CELL)-1), 0); - std::fill(fire_b[0]+0, fire_b[(YRES/CELL)-1]+((XRES/CELL)-1), 0); - std::fill(persistentVid, persistentVid+(VIDXRES*YRES), 0); -} - -void Renderer::AddRenderMode(unsigned int mode) -{ - for (size_t i = 0; i < render_modes.size(); i++) - { - if(render_modes[i] == mode) + if(!(render_mode & FIREMODE)) + return; + int i,j,x,y,r,g,b,a; + for (j=0; j(r, g, b, a)); + } + r *= 8; + g *= 8; + b *= 8; + for (y=-1; y<2; y++) + for (x=-1; x<2; x++) + if ((x || y) && i+x>=0 && j+y>=0 && i+x4 ? r-4 : 0; + fire_g[j][i] = g>4 ? g-4 : 0; + fire_b[j][i] = b>4 ? b-4 : 0; } - } - render_modes.push_back(mode); - CompileRenderMode(); } -void Renderer::RemoveRenderMode(unsigned int mode) +int HeatToColour(float temp) { - for (size_t i = 0; i < render_modes.size(); i++) - { - if(render_modes[i] == mode) - { - render_modes.erase(render_modes.begin() + i); - i = 0; - } - } - CompileRenderMode(); + constexpr float min_temp = MIN_TEMP; + constexpr float max_temp = MAX_TEMP; + RGB color = Renderer::heatTableAt(int((temp - min_temp) / (max_temp - min_temp) * 1024)); + color.Red = uint8_t(color.Red * 0.7f); + color.Green = uint8_t(color.Green * 0.7f); + color.Blue = uint8_t(color.Blue * 0.7f); + return color.Pack(); } - -void Renderer::SetRenderMode(std::vector render) -{ - render_modes = render; - CompileRenderMode(); -} - -std::vector Renderer::GetRenderMode() -{ - return render_modes; -} - -void Renderer::CompileDisplayMode() -{ - int old_display_mode = display_mode; - display_mode = 0; - for (size_t i = 0; i < display_modes.size(); i++) - display_mode |= display_modes[i]; - if (!(display_mode & DISPLAY_PERS) && (old_display_mode & DISPLAY_PERS)) - { - ClearAccumulation(); - } -} - -void Renderer::AddDisplayMode(unsigned int mode) -{ - for (size_t i = 0; i < display_modes.size(); i++) - { - if (display_modes[i] == mode) - { - return; - } - if (display_modes[i] & DISPLAY_AIR) - { - display_modes.erase(display_modes.begin()+i); - } - } - display_modes.push_back(mode); - CompileDisplayMode(); -} - -void Renderer::RemoveDisplayMode(unsigned int mode) -{ - for (size_t i = 0; i < display_modes.size(); i++) - { - if (display_modes[i] == mode) - { - display_modes.erase(display_modes.begin() + i); - i = 0; - } - } - CompileDisplayMode(); -} - -void Renderer::SetDisplayMode(std::vector display) -{ - display_modes = display; - CompileDisplayMode(); -} - -std::vector Renderer::GetDisplayMode() -{ - return display_modes; -} - -void Renderer::SetColourMode(unsigned int mode) -{ - colour_mode = mode; -} - -unsigned int Renderer::GetColourMode() -{ - return colour_mode; -} - -void Renderer::ResetModes() -{ - SetRenderMode({ RENDER_BASC, RENDER_FIRE, RENDER_SPRK, RENDER_EFFE }); - SetDisplayMode({ }); - SetColourMode(COLOUR_DEFAULT); -} - -VideoBuffer Renderer::DumpFrame() -{ - VideoBuffer newBuffer(XRES, YRES); - for(int y = 0; y < YRES; y++) - { - std::copy(vid+(y*WINDOWW), vid+(y*WINDOWW)+XRES, newBuffer.Buffer+(y*XRES)); - } - return newBuffer; -} - -Renderer::~Renderer() -{ - delete[] persistentVid; - delete[] warpVid; - delete[] graphicscache; -} - -#define PIXELMETHODS_CLASS Renderer - -#include "RasterDrawMethods.inl" - -#undef PIXELMETHODS_CLASS diff --git a/src/graphics/Renderer.h b/src/graphics/Renderer.h index 51957ce29..582a65cac 100644 --- a/src/graphics/Renderer.h +++ b/src/graphics/Renderer.h @@ -1,11 +1,12 @@ -#ifndef RENDERER_H -#define RENDERER_H -#include "Config.h" - +#pragma once +#include +#include +#include #include - #include "Graphics.h" #include "gui/interface/Point.h" +#include "common/tpt-rand.h" +#include "SimulationConfig.h" class RenderPreset; class Simulation; @@ -32,11 +33,36 @@ struct gcache_item }; typedef struct gcache_item gcache_item; -class Renderer +int HeatToColour(float temp); + +class Renderer: public RasterDrawMethods { + using Video = PlaneAdapter, WINDOW.X, RES.Y>; + Video video; + std::array persistentVideo; + Video warpVideo; + + Rect GetClipRect() const + { + return video.Size().OriginRect(); + } + + friend struct RasterDrawMethods; + public: + Vec2 Size() const + { + return video.Size(); + } + + pixel const *Data() const + { + return video.data(); + } + + RNG rng; + Simulation * sim; - Graphics * g; gcache_item *graphicscache; std::vector render_modes; @@ -46,9 +72,9 @@ public: unsigned int display_mode; std::vector renderModePresets; // - unsigned char fire_r[YRES/CELL][XRES/CELL]; - unsigned char fire_g[YRES/CELL][XRES/CELL]; - unsigned char fire_b[YRES/CELL][XRES/CELL]; + unsigned char fire_r[YCELLS][XCELLS]; + unsigned char fire_g[YCELLS][XCELLS]; + unsigned char fire_b[YCELLS][XCELLS]; unsigned int fire_alpha[CELL*3][CELL*3]; // bool gravityZonesEnabled; @@ -75,10 +101,10 @@ public: void RenderEnd(); void RenderZoom(); - void DrawBlob(int x, int y, unsigned char cr, unsigned char cg, unsigned char cb); + void DrawBlob(Vec2 pos, RGB colour); void DrawWalls(); void DrawSigns(); - void render_gravlensing(pixel * source); + void render_gravlensing(const Video &source); void render_fire(); void prepare_alpha(int size, float intensity); void render_parts(); @@ -89,43 +115,14 @@ public: void FinaliseParts(); void ClearAccumulation(); - void clearScreen(float alpha); - void SetSample(int x, int y); - - pixel * vid; - pixel * persistentVid; - pixel * warpVid; - void blendpixel(int x, int y, int r, int g, int b, int a); - void addpixel(int x, int y, int r, int g, int b, int a); + void clearScreen(); + void SetSample(Vec2 pos); void draw_icon(int x, int y, Icon icon); - int drawtext_outline(int x, int y, const String &s, int r, int g, int b, int a); - int drawtext(int x, int y, const String &s, int r, int g, int b, int a); - int drawchar(int x, int y, String::value_type c, int r, int g, int b, int a); - int addchar(int x, int y, String::value_type c, int r, int g, int b, int a); - - void xor_pixel(int x, int y); - void xor_line(int x, int y, int x2, int y2); - void xor_rect(int x, int y, int width, int height); - void xor_bitmap(unsigned char * bitmap, int x, int y, int w, int h); - - void draw_line(int x, int y, int x2, int y2, int r, int g, int b, int a); - void drawrect(int x, int y, int width, int height, int r, int g, int b, int a); - void fillrect(int x, int y, int width, int height, int r, int g, int b, int a); - void drawcircle(int x, int y, int rx, int ry, int r, int g, int b, int a); - void fillcircle(int x, int y, int rx, int ry, int r, int g, int b, int a); - void clearrect(int x, int y, int width, int height); - void gradientrect(int x, int y, int width, int height, int r, int g, int b, int a, int r2, int g2, int b2, int a2); - - void draw_image(const pixel *img, int x, int y, int w, int h, int a); - void draw_image(const VideoBuffer * vidBuf, int w, int h, int a); - VideoBuffer DumpFrame(); - void drawblob(int x, int y, unsigned char cr, unsigned char cg, unsigned char cb); - - pixel GetPixel(int x, int y); + pixel GetPixel(Vec2 pos) const; //... //Display mode modifiers void CompileDisplayMode(); @@ -146,14 +143,14 @@ public: int GetGridSize() { return gridSize; } void SetGridSize(int value) { gridSize = value; } - static VideoBuffer * WallIcon(int wallID, int width, int height); + static std::unique_ptr WallIcon(int wallID, Vec2 size); - Renderer(Graphics * g, Simulation * sim); + Renderer(Simulation * sim); ~Renderer(); #define RENDERER_TABLE(name) \ - static std::vector name; \ - static inline pixel name ## At(int index) \ + static std::vector> name; \ + static inline RGB name ## At(int index) \ { \ auto size = int(name.size()); \ if (index < 0) index = 0; \ @@ -171,5 +168,3 @@ public: private: int gridSize; }; - -#endif diff --git a/src/graphics/RendererBasic.cpp b/src/graphics/RendererBasic.cpp new file mode 100644 index 000000000..c9013d73c --- /dev/null +++ b/src/graphics/RendererBasic.cpp @@ -0,0 +1,475 @@ +#include +#include "gui/game/RenderPreset.h" +#include "RasterDrawMethodsImpl.h" +#include "Renderer.h" +#include "simulation/ElementClasses.h" +#include "simulation/ElementGraphics.h" +#include "simulation/Simulation.h" + +constexpr auto VIDXRES = WINDOWW; +constexpr auto VIDYRES = WINDOWH; + +void Renderer::RenderBegin() +{ + draw_air(); + draw_grav(); + DrawWalls(); + render_parts(); + + if(display_mode & DISPLAY_PERS) + { + std::transform(video.RowIterator({ 0, 0 }), video.RowIterator({ 0, YRES }), persistentVideo.begin(), [](pixel p) { + return RGB::Unpack(p).Decay().Pack(); + }); + } + + render_fire(); + draw_other(); + draw_grav_zones(); + DrawSigns(); + + FinaliseParts(); +} + +void Renderer::RenderEnd() +{ + RenderZoom(); +} + +void Renderer::SetSample(Vec2 pos) +{ + sampleColor = GetPixel(pos); +} + +void Renderer::clearScreen() { + if(display_mode & DISPLAY_PERS) + { + std::copy(persistentVideo.begin(), persistentVideo.end(), video.RowIterator({ 0, 0 })); + } + else + { + std::fill_n(video.data(), VIDXRES * YRES, 0); + } +} + +void Renderer::FinaliseParts() +{ + if(display_mode & DISPLAY_WARP) + { + warpVideo = video; + std::fill_n(video.data(), VIDXRES * YRES, 0); + render_gravlensing(warpVideo); + } +} + +void Renderer::RenderZoom() +{ + if(!zoomEnabled) + return; + { + int x, y, i, j; + pixel pix; + + DrawFilledRect(RectSized(zoomWindowPosition, { zoomScopeSize * ZFACTOR, zoomScopeSize * ZFACTOR }), 0x000000_rgb); + DrawRect(RectSized(zoomWindowPosition - Vec2{ 2, 2 }, Vec2{ zoomScopeSize*ZFACTOR+3, zoomScopeSize*ZFACTOR+3 }), 0xC0C0C0_rgb); + DrawRect(RectSized(zoomWindowPosition - Vec2{ 1, 1 }, Vec2{ zoomScopeSize*ZFACTOR+1, zoomScopeSize*ZFACTOR+1 }), 0x000000_rgb); + for (j=0; j pos, RGB colour) +{ + BlendPixel(pos + Vec2{ +1, 0 }, colour.WithAlpha(112)); + BlendPixel(pos + Vec2{ -1, 0 }, colour.WithAlpha(112)); + BlendPixel(pos + Vec2{ 0, 1 }, colour.WithAlpha(112)); + BlendPixel(pos + Vec2{ 0, -1 }, colour.WithAlpha(112)); + BlendPixel(pos + Vec2{ 1, -1 }, colour.WithAlpha(64)); + BlendPixel(pos + Vec2{ -1, -1 }, colour.WithAlpha(64)); + BlendPixel(pos + Vec2{ 1, 1 }, colour.WithAlpha(64)); + BlendPixel(pos + Vec2{ -1, +1 }, colour.WithAlpha(64)); +} + + +void Renderer::render_gravlensing(const Video &source) +{ + int nx, ny, rx, ry, gx, gy, bx, by, co; + for(nx = 0; nx < XRES; nx++) + { + for(ny = 0; ny < YRES; ny++) + { + co = (ny/CELL)*XCELLS+(nx/CELL); + rx = (int)(nx-sim->gravx[co]*0.75f+0.5f); + ry = (int)(ny-sim->gravy[co]*0.75f+0.5f); + gx = (int)(nx-sim->gravx[co]*0.875f+0.5f); + gy = (int)(ny-sim->gravy[co]*0.875f+0.5f); + bx = (int)(nx-sim->gravx[co]+0.5f); + by = (int)(ny-sim->gravy[co]+0.5f); + if(rx >= 0 && rx < XRES && ry >= 0 && ry < YRES && gx >= 0 && gx < XRES && gy >= 0 && gy < YRES && bx >= 0 && bx < XRES && by >= 0 && by < YRES) + { + auto t = RGB::Unpack(video[{ nx, ny }]); + t.Red = std::min(0xFF, (int)RGB::Unpack(source[{ rx, ry }]).Red + t.Red); + t.Green = std::min(0xFF, (int)RGB::Unpack(source[{ gx, gy }]).Green + t.Green); + t.Blue = std::min(0xFF, (int)RGB::Unpack(source[{ bx, by }]).Blue + t.Blue); + video[{ nx, ny }] = t.Pack(); + } + } + } +} + +float temp[CELL*3][CELL*3]; +float fire_alphaf[CELL*3][CELL*3]; +float glow_alphaf[11][11]; +float blur_alphaf[7][7]; +void Renderer::prepare_alpha(int size, float intensity) +{ + //TODO: implement size + int x,y,i,j; + float multiplier = 255.0f*intensity; + + memset(temp, 0, sizeof(temp)); + for (x=0; x pos) const +{ + if (pos.X<0 || pos.Y<0 || pos.X>=VIDXRES || pos.Y>=VIDYRES) + return 0; + return video[pos]; +} + +std::vector> Renderer::flameTable; +std::vector> Renderer::plasmaTable; +std::vector> Renderer::heatTable; +std::vector> Renderer::clfmTable; +std::vector> Renderer::firwTable; +static bool tablesPopulated = false; +static std::mutex tablesPopulatedMx; +void Renderer::PopulateTables() +{ + std::lock_guard g(tablesPopulatedMx); + if (!tablesPopulated) + { + tablesPopulated = true; + flameTable = Graphics::Gradient({ + { 0x000000_rgb, 0.00f }, + { 0x60300F_rgb, 0.50f }, + { 0xDFBF6F_rgb, 0.90f }, + { 0xAF9F0F_rgb, 1.00f }, + }, 200); + plasmaTable = Graphics::Gradient({ + { 0x000000_rgb, 0.00f }, + { 0x301040_rgb, 0.25f }, + { 0x301060_rgb, 0.50f }, + { 0xAFFFFF_rgb, 0.90f }, + { 0xAFFFFF_rgb, 1.00f }, + }, 200); + heatTable = Graphics::Gradient({ + { 0x2B00FF_rgb, 0.00f }, + { 0x003CFF_rgb, 0.01f }, + { 0x00C0FF_rgb, 0.05f }, + { 0x00FFEB_rgb, 0.08f }, + { 0x00FF14_rgb, 0.19f }, + { 0x4BFF00_rgb, 0.25f }, + { 0xC8FF00_rgb, 0.37f }, + { 0xFFDC00_rgb, 0.45f }, + { 0xFF0000_rgb, 0.71f }, + { 0xFF00DC_rgb, 1.00f }, + }, 1024); + clfmTable = Graphics::Gradient({ + { 0x000000_rgb, 0.00f }, + { 0x0A0917_rgb, 0.10f }, + { 0x19163C_rgb, 0.20f }, + { 0x28285E_rgb, 0.30f }, + { 0x343E77_rgb, 0.40f }, + { 0x49769A_rgb, 0.60f }, + { 0x57A0B4_rgb, 0.80f }, + { 0x5EC4C6_rgb, 1.00f }, + }, 200); + firwTable = Graphics::Gradient({ + { 0xFF00FF_rgb, 0.00f }, + { 0x0000FF_rgb, 0.20f }, + { 0x00FFFF_rgb, 0.40f }, + { 0x00FF00_rgb, 0.60f }, + { 0xFFFF00_rgb, 0.80f }, + { 0xFF0000_rgb, 1.00f }, + }, 200); + } +} + +Renderer::Renderer(Simulation * sim): + sim(NULL), + render_mode(0), + colour_mode(0), + display_mode(0), + gravityZonesEnabled(false), + gravityFieldEnabled(false), + decorations_enable(1), + blackDecorations(false), + debugLines(false), + sampleColor(0xFFFFFFFF), + findingElement(0), + foundElements(0), + mousePos(0, 0), + zoomWindowPosition(0, 0), + zoomScopePosition(0, 0), + zoomScopeSize(32), + zoomEnabled(false), + ZFACTOR(8), + gridSize(0) +{ + PopulateTables(); + + this->sim = sim; + + memset(fire_r, 0, sizeof(fire_r)); + memset(fire_g, 0, sizeof(fire_g)); + memset(fire_b, 0, sizeof(fire_b)); + + //Set defauly display modes + ResetModes(); + + //Render mode presets. Possibly load from config in future? + renderModePresets.push_back({ + "Alternative Velocity Display", + { RENDER_EFFE, RENDER_BASC }, + { DISPLAY_AIRC }, + 0 + }); + renderModePresets.push_back({ + "Velocity Display", + { RENDER_EFFE, RENDER_BASC }, + { DISPLAY_AIRV }, + 0 + }); + renderModePresets.push_back({ + "Pressure Display", + { RENDER_EFFE, RENDER_BASC }, + { DISPLAY_AIRP }, + 0 + }); + renderModePresets.push_back({ + "Persistent Display", + { RENDER_EFFE, RENDER_BASC }, + { DISPLAY_PERS }, + 0 + }); + renderModePresets.push_back({ + "Fire Display", + { RENDER_FIRE, RENDER_SPRK, RENDER_EFFE, RENDER_BASC }, + { }, + 0 + }); + renderModePresets.push_back({ + "Blob Display", + { RENDER_FIRE, RENDER_SPRK, RENDER_EFFE, RENDER_BLOB }, + { }, + 0 + }); + renderModePresets.push_back({ + "Heat Display", + { RENDER_BASC }, + { DISPLAY_AIRH }, + COLOUR_HEAT + }); + renderModePresets.push_back({ + "Fancy Display", + { RENDER_FIRE, RENDER_SPRK, RENDER_GLOW, RENDER_BLUR, RENDER_EFFE, RENDER_BASC }, + { DISPLAY_WARP }, + 0 + }); + renderModePresets.push_back({ + "Nothing Display", + { RENDER_BASC }, + { }, + 0 + }); + renderModePresets.push_back({ + "Heat Gradient Display", + { RENDER_BASC }, + { }, + COLOUR_GRAD + }); + renderModePresets.push_back({ + "Life Gradient Display", + { RENDER_BASC }, + { }, + COLOUR_LIFE + }); + + //Prepare the graphics cache + graphicscache = new gcache_item[PT_NUM]; + std::fill(&graphicscache[0], &graphicscache[0] + PT_NUM, gcache_item()); + + prepare_alpha(CELL, 1.0f); +} + +void Renderer::CompileRenderMode() +{ + int old_render_mode = render_mode; + render_mode = 0; + for (size_t i = 0; i < render_modes.size(); i++) + render_mode |= render_modes[i]; + + //If firemode is removed, clear the fire display + if(!(render_mode & FIREMODE) && (old_render_mode & FIREMODE)) + { + ClearAccumulation(); + } +} + +void Renderer::ClearAccumulation() +{ + std::fill(&fire_r[0][0], &fire_r[0][0] + NCELL, 0); + std::fill(&fire_g[0][0], &fire_g[0][0] + NCELL, 0); + std::fill(&fire_b[0][0], &fire_b[0][0] + NCELL, 0); + std::fill(persistentVideo.begin(), persistentVideo.end(), 0); +} + +void Renderer::AddRenderMode(unsigned int mode) +{ + for (size_t i = 0; i < render_modes.size(); i++) + { + if(render_modes[i] == mode) + { + return; + } + } + render_modes.push_back(mode); + CompileRenderMode(); +} + +void Renderer::RemoveRenderMode(unsigned int mode) +{ + for (size_t i = 0; i < render_modes.size(); i++) + { + if(render_modes[i] == mode) + { + render_modes.erase(render_modes.begin() + i); + i = 0; + } + } + CompileRenderMode(); +} + +void Renderer::SetRenderMode(std::vector render) +{ + render_modes = render; + CompileRenderMode(); +} + +std::vector Renderer::GetRenderMode() +{ + return render_modes; +} + +void Renderer::CompileDisplayMode() +{ + int old_display_mode = display_mode; + display_mode = 0; + for (size_t i = 0; i < display_modes.size(); i++) + display_mode |= display_modes[i]; + if (!(display_mode & DISPLAY_PERS) && (old_display_mode & DISPLAY_PERS)) + { + ClearAccumulation(); + } +} + +void Renderer::AddDisplayMode(unsigned int mode) +{ + for (size_t i = 0; i < display_modes.size(); i++) + { + if (display_modes[i] == mode) + { + return; + } + if (display_modes[i] & DISPLAY_AIR) + { + display_modes.erase(display_modes.begin()+i); + } + } + display_modes.push_back(mode); + CompileDisplayMode(); +} + +void Renderer::RemoveDisplayMode(unsigned int mode) +{ + for (size_t i = 0; i < display_modes.size(); i++) + { + if (display_modes[i] == mode) + { + display_modes.erase(display_modes.begin() + i); + i = 0; + } + } + CompileDisplayMode(); +} + +void Renderer::SetDisplayMode(std::vector display) +{ + display_modes = display; + CompileDisplayMode(); +} + +std::vector Renderer::GetDisplayMode() +{ + return display_modes; +} + +void Renderer::SetColourMode(unsigned int mode) +{ + colour_mode = mode; +} + +unsigned int Renderer::GetColourMode() +{ + return colour_mode; +} + +void Renderer::ResetModes() +{ + SetRenderMode({ RENDER_BASC, RENDER_FIRE, RENDER_SPRK, RENDER_EFFE }); + SetDisplayMode({ }); + SetColourMode(COLOUR_DEFAULT); +} + +VideoBuffer Renderer::DumpFrame() +{ + VideoBuffer newBuffer(RES); + newBuffer.BlendImage(video.data(), 0xFF, Size().OriginRect()); + return newBuffer; +} + +Renderer::~Renderer() +{ + delete[] graphicscache; +} + +template struct RasterDrawMethods; diff --git a/src/graphics/RendererFont.cpp b/src/graphics/RendererFont.cpp new file mode 100644 index 000000000..9671d36d3 --- /dev/null +++ b/src/graphics/RendererFont.cpp @@ -0,0 +1,33 @@ +#include "Renderer.h" + +void Renderer::draw_air() +{ +} + +void Renderer::draw_grav() +{ +} + +void Renderer::DrawWalls() +{ +} + +void Renderer::render_parts() +{ +} + +void Renderer::render_fire() +{ +} + +void Renderer::draw_other() +{ +} + +void Renderer::draw_grav_zones() +{ +} + +void Renderer::DrawSigns() +{ +} diff --git a/src/graphics/meson.build b/src/graphics/meson.build index 1a7e4fb9f..5c01cffc6 100644 --- a/src/graphics/meson.build +++ b/src/graphics/meson.build @@ -1,11 +1,17 @@ graphics_files = files( 'Graphics.cpp', - #'OpenGLGraphics.cpp', # this is defunct right now 'RasterGraphics.cpp', 'FontReader.cpp', +) +powder_graphics_files = files( + 'RendererBasic.cpp', 'Renderer.cpp', ) +font_graphics_files = files( + 'RendererBasic.cpp', + 'RendererFont.cpp', +) -powder_files += graphics_files -render_files += graphics_files -font_files += graphics_files +powder_files += graphics_files + powder_graphics_files +render_files += graphics_files + powder_graphics_files +font_files += graphics_files + font_graphics_files diff --git a/src/gui/Style.h b/src/gui/Style.h index 6c26dbdda..c04eb7fa4 100644 --- a/src/gui/Style.h +++ b/src/gui/Style.h @@ -1,7 +1,4 @@ -#ifndef STYLE_H_ -#define STYLE_H_ -#include "Config.h" - +#pragma once #include "gui/interface/Colour.h" namespace style @@ -26,5 +23,3 @@ namespace style { }; } - -#endif diff --git a/src/gui/colourpicker/ColourPickerActivity.cpp b/src/gui/colourpicker/ColourPickerActivity.cpp index 19f105159..636317248 100644 --- a/src/gui/colourpicker/ColourPickerActivity.cpp +++ b/src/gui/colourpicker/ColourPickerActivity.cpp @@ -4,13 +4,14 @@ #include "gui/interface/Slider.h" #include "gui/interface/Button.h" #include "gui/interface/Label.h" -#include "gui/interface/Keys.h" #include "gui/Style.h" #include "graphics/Graphics.h" #include "Misc.h" +#include + ColourPickerActivity::ColourPickerActivity(ui::Colour initialColour, OnPicked onPicked_) : WindowActivity(ui::Point(-1, -1), ui::Point(266, 215)), currentHue(0), @@ -254,15 +255,13 @@ void ColourPickerActivity::OnKeyPress(int key, int scan, bool repeat, bool shift void ColourPickerActivity::OnDraw() { Graphics * g = GetGraphics(); - //g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->fillrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3, 0, 0, 0, currentAlpha); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->BlendFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb .WithAlpha(currentAlpha)); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); - g->drawrect(Position.X+4, Position.Y+4, 258, 130, 180, 180, 180, 255); + g->DrawRect(RectSized(Position + Vec2{ 4, 4 }, Vec2{ 258, 130 }), 0xB4B4B4_rgb); - int offsetX = Position.X+5; - int offsetY = Position.Y+5; + auto offset = Position + Vec2{ 5, 5 }; //draw color square @@ -271,7 +270,7 @@ void ColourPickerActivity::OnDraw() { for(int hue = 0; hue <= 359; hue++) { - currx = clamp_flt(float(hue), 0, 359)+offsetX; + currx = clamp_flt(float(hue), 0, 359)+offset.X; if (currx == lastx) continue; lastx = currx; @@ -279,7 +278,7 @@ void ColourPickerActivity::OnDraw() int cg = 0; int cb = 0; HSV_to_RGB(hue, 255-saturation, currentValue, &cr, &cg, &cb); - g->blendpixel(currx, (saturation/2)+offsetY, cr, cg, cb, currentAlpha); + g->BlendPixel({ currx, (saturation/2)+offset.Y }, RGBA(cr, cg, cb, currentAlpha)); } } @@ -292,10 +291,9 @@ void ColourPickerActivity::OnDraw() HSV_to_RGB(hue, currentSaturation, currentValue, &red, &green, &blue); for (int ry = 0; ry < (hSlider->Size.Y / 2) - 1; ry++) { - g->blendpixel( - rx + offsetX + hSlider->Position.X, - ry + offsetY + hSlider->Position.Y, - red, green, blue, currentAlpha + g->BlendPixel( + offset + hSlider->Position + Vec2{ rx, ry }, + RGBA(red, green, blue, currentAlpha) ); } } @@ -303,9 +301,9 @@ void ColourPickerActivity::OnDraw() //draw color square pointer int currentHueX = clamp_flt(float(currentHue), 0, 359); int currentSaturationY = ((255-currentSaturation)/2); - g->xor_line(offsetX+currentHueX, offsetY+currentSaturationY-5, offsetX+currentHueX, offsetY+currentSaturationY-1); - g->xor_line(offsetX+currentHueX, offsetY+currentSaturationY+1, offsetX+currentHueX, offsetY+currentSaturationY+5); - g->xor_line(offsetX+currentHueX-5, offsetY+currentSaturationY, offsetX+currentHueX-1, offsetY+currentSaturationY); - g->xor_line(offsetX+currentHueX+1, offsetY+currentSaturationY, offsetX+currentHueX+5, offsetY+currentSaturationY); + g->XorLine(offset + Vec2{ currentHueX, currentSaturationY-5 }, offset + Vec2{ currentHueX, currentSaturationY-1 }); + g->XorLine(offset + Vec2{ currentHueX, currentSaturationY+1 }, offset + Vec2{ currentHueX, currentSaturationY+5 }); + g->XorLine(offset + Vec2{ currentHueX-5, currentSaturationY }, offset + Vec2{ currentHueX-1, currentSaturationY }); + g->XorLine(offset + Vec2{ currentHueX+1, currentSaturationY }, offset + Vec2{ currentHueX+5, currentSaturationY }); } diff --git a/src/gui/console/ConsoleCommand.h b/src/gui/console/ConsoleCommand.h index 95e438715..2a7c0f305 100644 --- a/src/gui/console/ConsoleCommand.h +++ b/src/gui/console/ConsoleCommand.h @@ -1,6 +1,4 @@ -#ifndef CONSOLECOMMAND_H_ -#define CONSOLECOMMAND_H_ - +#pragma once #include "common/String.h" class ConsoleCommand @@ -15,11 +13,9 @@ public: int ReturnStatus; String ReturnValue; - operator ByteString() const + operator String() const { - return Command.ToUtf8(); + return Command; } }; - -#endif /* CONSOLECOMMAND_H_ */ diff --git a/src/gui/console/ConsoleController.cpp b/src/gui/console/ConsoleController.cpp index 0a31c798f..47a4d0327 100644 --- a/src/gui/console/ConsoleController.cpp +++ b/src/gui/console/ConsoleController.cpp @@ -71,8 +71,10 @@ ConsoleView * ConsoleController::GetView() ConsoleController::~ConsoleController() { - consoleView->CloseActiveWindow(); delete consoleModel; - delete consoleView; + if (consoleView->CloseActiveWindow()) + { + delete consoleView; + } } diff --git a/src/gui/console/ConsoleController.h b/src/gui/console/ConsoleController.h index 656e952d4..66527df78 100644 --- a/src/gui/console/ConsoleController.h +++ b/src/gui/console/ConsoleController.h @@ -1,9 +1,5 @@ -#ifndef CONSOLECONTROLLER_H_ -#define CONSOLECONTROLLER_H_ -#include "Config.h" - +#pragma once #include "common/String.h" - #include class CommandInterface; @@ -27,5 +23,3 @@ public: ConsoleView * GetView(); virtual ~ConsoleController(); }; - -#endif /* CONSOLECONTROLLER_H_ */ diff --git a/src/gui/console/ConsoleModel.cpp b/src/gui/console/ConsoleModel.cpp index e92245fda..639c2df3d 100644 --- a/src/gui/console/ConsoleModel.cpp +++ b/src/gui/console/ConsoleModel.cpp @@ -1,11 +1,9 @@ #include "ConsoleModel.h" - #include "ConsoleView.h" - -#include "client/Client.h" +#include "prefs/GlobalPrefs.h" ConsoleModel::ConsoleModel() { - std::vector previousHistory = Client::Ref().GetPrefStringArray("Console.History"); + std::vector previousHistory = GlobalPrefs::Ref().Get("Console.History", std::vector{}); for(std::vector::reverse_iterator iter = previousHistory.rbegin(), end = previousHistory.rend(); iter != end; ++iter) { if(previousCommands.size()<25) @@ -48,7 +46,7 @@ void ConsoleModel::AddLastCommand(ConsoleCommand command) if(previousCommands.size()>25) previousCommands.pop_front(); currentCommandIndex = previousCommands.size(); - Client::Ref().SetPref("Console.History", std::vector(previousCommands.begin(), previousCommands.end())); + GlobalPrefs::Ref().Set("Console.History", std::vector(previousCommands.begin(), previousCommands.end())); notifyPreviousCommandsChanged(); } diff --git a/src/gui/console/ConsoleModel.h b/src/gui/console/ConsoleModel.h index 33957fc9c..af731d7b7 100644 --- a/src/gui/console/ConsoleModel.h +++ b/src/gui/console/ConsoleModel.h @@ -1,10 +1,7 @@ -#ifndef CONSOLEMODEL_H_ -#define CONSOLEMODEL_H_ -#include "Config.h" - +#pragma once +#include "ConsoleCommand.h" #include #include -#include "ConsoleCommand.h" class ConsoleView; class ConsoleModel @@ -24,5 +21,3 @@ public: void AddObserver(ConsoleView * observer); void AddLastCommand(ConsoleCommand command); }; - -#endif /* CONSOLEMODEL_H_ */ diff --git a/src/gui/console/ConsoleView.cpp b/src/gui/console/ConsoleView.cpp index 9c2848199..c6529c00a 100644 --- a/src/gui/console/ConsoleView.cpp +++ b/src/gui/console/ConsoleView.cpp @@ -1,19 +1,14 @@ #include "ConsoleView.h" - #include "ConsoleController.h" #include "ConsoleModel.h" - -#include - #include "graphics/Graphics.h" - -#include "Config.h" - #include "ConsoleCommand.h" - -#include "gui/interface/Keys.h" #include "gui/interface/Label.h" #include "gui/interface/Textbox.h" +#include "gui/interface/Engine.h" +#include "SimulationConfig.h" +#include +#include ConsoleView::ConsoleView(): ui::Window(ui::Point(0, 0), ui::Point(WINDOWW, 150)), @@ -30,7 +25,7 @@ ConsoleView::ConsoleView(): void ConsoleView::DoKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) { - if ((scan == SDL_SCANCODE_GRAVE && key != '~') || key == SDLK_ESCAPE) + if ((ui::Engine::Ref().GraveExitsConsole && scan == SDL_SCANCODE_GRAVE && key != '~') || key == SDLK_ESCAPE) { if (!repeat) doClose = true; @@ -116,9 +111,9 @@ void ConsoleView::NotifyCurrentCommandChanged(ConsoleModel * sender) void ConsoleView::OnDraw() { Graphics * g = GetGraphics(); - g->fillrect(Position.X, Position.Y, Size.X, Size.Y, 0, 0, 0, 110); - g->draw_line(Position.X, Position.Y+Size.Y-16, Position.X+Size.X, Position.Y+Size.Y-16, 255, 255, 255, 160); - g->draw_line(Position.X, Position.Y+Size.Y, Position.X+Size.X, Position.Y+Size.Y, 255, 255, 255, 200); + g->BlendFilledRect(RectSized(Position, Size), 0x000000_rgb .WithAlpha(110)); + g->BlendLine(Position + Vec2{ 0, Size.Y-16 }, Position + Size - Vec2{ 0, 16 }, 0xFFFFFF_rgb .WithAlpha(160)); + g->BlendLine(Position + Vec2{ 0, Size.Y }, Position + Size, 0xFFFFFF_rgb .WithAlpha(200)); } void ConsoleView::OnTick(float dt) diff --git a/src/gui/console/ConsoleView.h b/src/gui/console/ConsoleView.h index 2a0b0a139..4fae3262f 100644 --- a/src/gui/console/ConsoleView.h +++ b/src/gui/console/ConsoleView.h @@ -1,8 +1,6 @@ -#ifndef CONSOLEVIEW_H_ -#define CONSOLEVIEW_H_ - -#include +#pragma once #include "gui/interface/Window.h" +#include namespace ui { @@ -31,5 +29,3 @@ public: void NotifyCurrentCommandChanged(ConsoleModel * sender); virtual ~ConsoleView(); }; - -#endif /* CONSOLEVIEW_H_ */ diff --git a/src/gui/dialogues/ConfirmPrompt.cpp b/src/gui/dialogues/ConfirmPrompt.cpp index 5c5b804cc..845b4e8b6 100644 --- a/src/gui/dialogues/ConfirmPrompt.cpp +++ b/src/gui/dialogues/ConfirmPrompt.cpp @@ -7,7 +7,7 @@ #include "gui/interface/Label.h" #include "gui/interface/ScrollPanel.h" -#include "PowderToy.h" +#include "PowderToySDL.h" #include "graphics/Graphics.h" @@ -36,7 +36,7 @@ ConfirmPrompt::ConfirmPrompt(String title, String message, ResultCallback callba if (messageLabel->Size.Y < messagePanel->Size.Y) messagePanel->Size.Y = messageLabel->Size.Y+4; Size.Y += messagePanel->Size.Y+12; - Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + Position.Y = (GetGraphics()->Size().Y - Size.Y)/2; ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X-75, 16), "Cancel"); cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; @@ -82,6 +82,6 @@ void ConfirmPrompt::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); } diff --git a/src/gui/dialogues/ConfirmPrompt.h b/src/gui/dialogues/ConfirmPrompt.h index ef84c0c49..17a45ef5f 100644 --- a/src/gui/dialogues/ConfirmPrompt.h +++ b/src/gui/dialogues/ConfirmPrompt.h @@ -1,6 +1,4 @@ -#ifndef CONFIRMPROMPT_H_ -#define CONFIRMPROMPT_H_ - +#pragma once #include "gui/interface/Window.h" #include @@ -21,5 +19,3 @@ public: static bool Blocking(String title, String message, String buttonText = String("Confirm")); void OnDraw() override; }; - -#endif /* CONFIRMPROMPT_H_ */ diff --git a/src/gui/dialogues/ErrorMessage.cpp b/src/gui/dialogues/ErrorMessage.cpp index 557e2301f..15a726d1b 100644 --- a/src/gui/dialogues/ErrorMessage.cpp +++ b/src/gui/dialogues/ErrorMessage.cpp @@ -6,7 +6,7 @@ #include "gui/interface/Engine.h" #include "gui/interface/Label.h" -#include "PowderToy.h" +#include "PowderToySDL.h" #include "graphics/Graphics.h" @@ -27,7 +27,7 @@ ErrorMessage::ErrorMessage(String title, String message, DismissCallback callbac AddComponent(messageLabel); Size.Y += messageLabel->Size.Y+12; - Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + Position.Y = (GetGraphics()->Size().Y - Size.Y)/2; ui::Button * okayButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "Dismiss"); okayButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; @@ -58,6 +58,6 @@ void ErrorMessage::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); } diff --git a/src/gui/dialogues/ErrorMessage.h b/src/gui/dialogues/ErrorMessage.h index 003258554..fcec273ed 100644 --- a/src/gui/dialogues/ErrorMessage.h +++ b/src/gui/dialogues/ErrorMessage.h @@ -1,6 +1,4 @@ -#ifndef ERRORMESSAGE_H_ -#define ERRORMESSAGE_H_ - +#pragma once #include "gui/interface/Window.h" #include @@ -21,5 +19,3 @@ public: static void Blocking(String title, String message); void OnDraw() override; }; - -#endif /* ERRORMESSAGE_H_ */ diff --git a/src/gui/dialogues/InformationMessage.cpp b/src/gui/dialogues/InformationMessage.cpp index 3257a610d..20d283bee 100644 --- a/src/gui/dialogues/InformationMessage.cpp +++ b/src/gui/dialogues/InformationMessage.cpp @@ -46,7 +46,7 @@ InformationMessage::InformationMessage(String title, String message, bool large) if (messageLabel->Size.Y < messagePanel->Size.Y) messagePanel->Size.Y = messageLabel->Size.Y+4; Size.Y += messagePanel->Size.Y+12; - Position.Y = (ui::Engine::Ref().GetHeight()-Size.Y)/2; + Position.Y = (GetGraphics()->Size().Y - Size.Y) / 2; } ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), title); @@ -74,6 +74,6 @@ void InformationMessage::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); } diff --git a/src/gui/dialogues/InformationMessage.h b/src/gui/dialogues/InformationMessage.h index 1570d6f1d..12a440432 100644 --- a/src/gui/dialogues/InformationMessage.h +++ b/src/gui/dialogues/InformationMessage.h @@ -1,6 +1,4 @@ -#ifndef INFORMATIONMESSAGE_H_ -#define INFORMATIONMESSAGE_H_ - +#pragma once #include "gui/interface/Window.h" class InformationMessage : public ui::Window @@ -11,5 +9,3 @@ public: void OnDraw() override; }; - -#endif /* INFORMATIONMESSAGE_H_ */ diff --git a/src/gui/dialogues/SaveIDMessage.cpp b/src/gui/dialogues/SaveIDMessage.cpp index 95b9a1c1c..bf48d1f5b 100644 --- a/src/gui/dialogues/SaveIDMessage.cpp +++ b/src/gui/dialogues/SaveIDMessage.cpp @@ -1,38 +1,35 @@ #include "SaveIDMessage.h" - #include "gui/Style.h" - #include "graphics/Graphics.h" - #include "gui/interface/Button.h" #include "gui/interface/CopyTextButton.h" #include "gui/interface/Label.h" - #include "Format.h" +#include "SimulationConfig.h" SaveIDMessage::SaveIDMessage(int id): ui::Window(ui::Point((XRES-244)/2, (YRES-90)/2), ui::Point(244, 90)) { - int textWidth = Graphics::textwidth("Save ID"); + int textWidth = Graphics::TextSize("Save ID").X - 1; ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(textWidth+20, 16), "Save ID"); titleLabel->SetTextColour(style::Colour::InformationTitle); titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; AddComponent(titleLabel); - textWidth = Graphics::textwidth("Saved Successfully!"); + textWidth = Graphics::TextSize("Saved Successfully!").X - 1; ui::Label * messageLabel = new ui::Label(ui::Point(4, 24), ui::Point(textWidth+20, 16), "Saved Successfully!"); messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; messageLabel->Appearance.VerticalAlign = ui::Appearance::AlignTop; AddComponent(messageLabel); - textWidth = Graphics::textwidth("Click the box below to copy the save ID"); + textWidth = Graphics::TextSize("Click the box below to copy the save ID").X - 1; ui::Label * copyTextLabel = new ui::Label(ui::Point((Size.X-textWidth-20)/2, 35), ui::Point(textWidth+20, 16), "Click the box below to copy the save id"); copyTextLabel->SetTextColour(ui::Colour(150, 150, 150)); copyTextLabel->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; AddComponent(copyTextLabel); - textWidth = Graphics::textwidth(String::Build(id)); + textWidth = Graphics::TextSize(String::Build(id)).X - 1; ui::CopyTextButton * copyTextButton = new ui::CopyTextButton(ui::Point((Size.X-textWidth-10)/2, 50), ui::Point(textWidth+10, 18), String::Build(id), copyTextLabel); AddComponent(copyTextButton); @@ -55,8 +52,8 @@ void SaveIDMessage::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); } void SaveIDMessage::OnTryExit(ExitMethod method) diff --git a/src/gui/dialogues/SaveIDMessage.h b/src/gui/dialogues/SaveIDMessage.h index bf4c7b24e..f38603649 100644 --- a/src/gui/dialogues/SaveIDMessage.h +++ b/src/gui/dialogues/SaveIDMessage.h @@ -1,6 +1,4 @@ -#ifndef SAVEIDMESSAGE_H -#define SAVEIDMESSAGE_H - +#pragma once #include "gui/interface/Window.h" class SaveIDMessage : public ui::Window @@ -12,5 +10,3 @@ public: void OnDraw() override; void OnTryExit(ExitMethod method) override; }; - -#endif /* SAVEIDMESSAGE_H */ diff --git a/src/gui/dialogues/TextPrompt.cpp b/src/gui/dialogues/TextPrompt.cpp index 823fe29c0..cd5b7a490 100644 --- a/src/gui/dialogues/TextPrompt.cpp +++ b/src/gui/dialogues/TextPrompt.cpp @@ -6,7 +6,7 @@ #include "gui/interface/Textbox.h" #include "gui/Style.h" -#include "PowderToy.h" +#include "PowderToySDL.h" #include "graphics/Graphics.h" @@ -93,6 +93,6 @@ void TextPrompt::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); } diff --git a/src/gui/dialogues/TextPrompt.h b/src/gui/dialogues/TextPrompt.h index c86699d29..3fbeaf2de 100644 --- a/src/gui/dialogues/TextPrompt.h +++ b/src/gui/dialogues/TextPrompt.h @@ -1,6 +1,4 @@ -#ifndef TEXTPROMPT_H_ -#define TEXTPROMPT_H_ - +#pragma once #include "gui/interface/Window.h" #include @@ -30,5 +28,3 @@ public: static String Blocking(String title, String message, String text, String placeholder, bool multiline); void OnDraw() override; }; - -#endif /* TEXTPROMPT_H_ */ diff --git a/src/gui/elementsearch/ElementSearchActivity.cpp b/src/gui/elementsearch/ElementSearchActivity.cpp index 0afc4b6ab..f0f86e94b 100644 --- a/src/gui/elementsearch/ElementSearchActivity.cpp +++ b/src/gui/elementsearch/ElementSearchActivity.cpp @@ -3,10 +3,10 @@ #include #include #include +#include #include "gui/interface/Textbox.h" #include "gui/interface/Label.h" -#include "gui/interface/Keys.h" #include "gui/game/Tool.h" #include "gui/game/Menu.h" #include "gui/Style.h" @@ -127,9 +127,9 @@ void ElementSearchActivity::searchTools(String query) for (int toolIndex = 0; toolIndex < (int)tools.size(); ++toolIndex) { - int favouritePriority = favs.find(tools[toolIndex]->GetIdentifier()) != favs.end() ? 0 : 1; - pushIfMatches(tools[toolIndex]->GetName().ToLower(), toolIndex, favouritePriority, 0); - pushIfMatches(tools[toolIndex]->GetDescription().ToLower(), toolIndex, favouritePriority, 1); + int favouritePriority = favs.find(tools[toolIndex]->Identifier) != favs.end() ? 0 : 1; + pushIfMatches(tools[toolIndex]->Name.ToLower(), toolIndex, favouritePriority, 0); + pushIfMatches(tools[toolIndex]->Description.ToLower(), toolIndex, favouritePriority, 1); auto it = menudescriptionLower.find(tools[toolIndex]); if (it != menudescriptionLower.end()) { @@ -149,16 +149,16 @@ void ElementSearchActivity::searchTools(String query) if(!firstResult) firstResult = tool; - VideoBuffer * tempTexture = tool->GetTexture(26, 14); + std::unique_ptr tempTexture = tool->GetTexture(Vec2(26, 14)); ToolButton * tempButton; if(tempTexture) - tempButton = new ToolButton(current+viewPosition, ui::Point(30, 18), "", tool->GetIdentifier(), tool->GetDescription()); + tempButton = new ToolButton(current+viewPosition, ui::Point(30, 18), "", tool->Identifier, tool->Description); else - tempButton = new ToolButton(current+viewPosition, ui::Point(30, 18), tool->GetName(), tool->GetIdentifier(), tool->GetDescription()); + tempButton = new ToolButton(current+viewPosition, ui::Point(30, 18), tool->Name, tool->Identifier, tool->Description); - tempButton->Appearance.SetTexture(tempTexture); - tempButton->Appearance.BackgroundInactive = ui::Colour(tool->colRed, tool->colGreen, tool->colBlue); + tempButton->Appearance.SetTexture(std::move(tempTexture)); + tempButton->Appearance.BackgroundInactive = tool->Colour.WithAlpha(0xFF); tempButton->SetActionCallback({ [this, tempButton, tool] { if (tempButton->GetSelectionState() >= 0 && tempButton->GetSelectionState() <= 2) SetActiveTool(tempButton->GetSelectionState(), tool); @@ -196,11 +196,11 @@ void ElementSearchActivity::SetActiveTool(int selectionState, Tool * tool) { if (ctrlPressed && shiftPressed && !altPressed) { - Favorite::Ref().AddFavorite(tool->GetIdentifier()); + Favorite::Ref().AddFavorite(tool->Identifier); gameController->RebuildFavoritesMenu(); } else if (ctrlPressed && altPressed && !shiftPressed && - tool->GetIdentifier().Contains("DEFAULT_PT_")) + tool->Identifier.BeginsWith("DEFAULT_PT_")) { gameController->SetActiveTool(3, tool); } @@ -212,13 +212,16 @@ void ElementSearchActivity::SetActiveTool(int selectionState, Tool * tool) void ElementSearchActivity::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); - g->drawrect(Position.X+searchField->Position.X, Position.Y+searchField->Position.Y+searchField->Size.Y+8, searchField->Size.X, Size.Y-(searchField->Position.Y+searchField->Size.Y+8)-23, 255, 255, 255, 180); + 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 }), + 0xFFFFFF_rgb .WithAlpha(180)); if (toolTipPresence && toolTip.length()) { - g->drawtext(10, Size.Y+70, toolTip, 255, 255, 255, toolTipPresence>51?255:toolTipPresence*5); + g->BlendText({ 10, Size.Y+70 }, toolTip, 0xFFFFFF_rgb .WithAlpha(toolTipPresence>51?255:toolTipPresence*5)); } } diff --git a/src/gui/elementsearch/ElementSearchActivity.h b/src/gui/elementsearch/ElementSearchActivity.h index 09444b143..049698f02 100644 --- a/src/gui/elementsearch/ElementSearchActivity.h +++ b/src/gui/elementsearch/ElementSearchActivity.h @@ -1,10 +1,8 @@ -#ifndef ELEMENTSEARCHACTIVITY_H_ -#define ELEMENTSEARCHACTIVITY_H_ - -#include +#pragma once #include "Activity.h" #include "common/String.h" #include "gui/interface/Point.h" +#include class Tool; class ToolButton; @@ -42,5 +40,3 @@ public: void OnDraw() override; void ToolTip(ui::Point senderPosition, String ToolTip) override; }; - -#endif /* ELEMENTSEARCHACTIVITY_H_ */ diff --git a/src/gui/filebrowser/FileBrowserActivity.cpp b/src/gui/filebrowser/FileBrowserActivity.cpp index ff48170fa..668e811e1 100644 --- a/src/gui/filebrowser/FileBrowserActivity.cpp +++ b/src/gui/filebrowser/FileBrowserActivity.cpp @@ -1,10 +1,8 @@ #include "FileBrowserActivity.h" -#include - #include "client/GameSave.h" #include "client/SaveFile.h" -#include "common/Platform.h" +#include "common/platform/Platform.h" #include "graphics/Graphics.h" #include "gui/Style.h" #include "tasks/Task.h" @@ -18,12 +16,15 @@ #include "gui/interface/ScrollPanel.h" #include "gui/interface/Textbox.h" +#include "Config.h" +#include + //Currently, reading is done on another thread, we can't render outside the main thread due to some bullshit with OpenGL class LoadFilesTask: public Task { ByteString directory; ByteString search; - std::vector saveFiles; + std::vector> saveFiles; void before() override { @@ -43,20 +44,20 @@ class LoadFilesTask: public Task notifyProgress(-1); for(std::vector::iterator iter = files.begin(), end = files.end(); iter != end; ++iter) { - SaveFile * saveFile = new SaveFile(directory + *iter, true); - saveFiles.push_back(saveFile); + auto saveFile = std::make_unique(directory + *iter, true); - ByteString filename = (*iter).SplitFromEndBy(PATH_SEP).After(); + ByteString filename = (*iter).SplitFromEndBy(PATH_SEP_CHAR).After(); filename = filename.SplitFromEndBy('.').Before(); saveFile->SetDisplayName(filename.FromUtf8()); + saveFiles.push_back(std::move(saveFile)); } return true; } public: - std::vector GetSaveFiles() + std::vector> TakeSaveFiles() { - return saveFiles; + return std::move(saveFiles); } LoadFilesTask(ByteString directory, ByteString search): @@ -127,31 +128,36 @@ void FileBrowserActivity::DoSearch(ByteString search) } } -void FileBrowserActivity::SelectSave(SaveFile * file) +void FileBrowserActivity::SelectSave(int index) { if (onSelected) - onSelected(std::unique_ptr(new SaveFile(*file))); + { + auto file = std::move(files[index]); + files.clear(); + onSelected(std::move(file)); + } Exit(); } -void FileBrowserActivity::DeleteSave(SaveFile * file) +void FileBrowserActivity::DeleteSave(int index) { + auto &file = files[index]; String deleteMessage = "Are you sure you want to delete " + file->GetDisplayName() + ".cps?"; if (ConfirmPrompt::Blocking("Delete Save", deleteMessage)) { - remove(file->GetName().c_str()); + Platform::RemoveFile(file->GetName()); loadDirectory(directory, ""); } } -void FileBrowserActivity::RenameSave(SaveFile * file) +void FileBrowserActivity::RenameSave(int index) { + auto &file = files[index]; ByteString newName = TextPrompt::Blocking("Rename", "Change save name", file->GetDisplayName(), "", 0).ToUtf8(); if (newName.length()) { - newName = directory + PATH_SEP + newName + ".cps"; - int ret = rename(file->GetName().c_str(), newName.c_str()); - if (ret) + newName = ByteString::Build(directory, PATH_SEP_CHAR, newName, ".cps"); + if (!Platform::RenameFile(file->GetName(), newName, false)) ErrorMessage::Blocking("Error", "Could not rename file"); else loadDirectory(directory, ""); @@ -168,10 +174,6 @@ void FileBrowserActivity::cleanup() } componentsQueue.clear(); - for (auto file : files) - { - delete file; - } files.clear(); } @@ -199,7 +201,8 @@ void FileBrowserActivity::NotifyDone(Task * task) { fileX = 0; fileY = 0; - files = ((LoadFilesTask*)task)->GetSaveFiles(); + files = ((LoadFilesTask*)task)->TakeSaveFiles(); + createButtons = true; totalFiles = files.size(); delete loadFiles; loadFiles = NULL; @@ -254,35 +257,37 @@ void FileBrowserActivity::OnTick(float dt) if(loadFiles) loadFiles->Poll(); - while(files.size()) + if (createButtons) { - SaveFile * saveFile = files.back(); - files.pop_back(); - - if(fileX == filesX) + createButtons = false; + for (auto i = 0; i < int(files.size()); ++i) { - fileX = 0; - fileY++; - } - ui::SaveButton * saveButton = new ui::SaveButton( - ui::Point( - buttonXOffset + buttonPadding + fileX*(buttonWidth+buttonPadding*2), - buttonYOffset + buttonPadding + fileY*(buttonHeight+buttonPadding*2) - ), - ui::Point(buttonWidth, buttonHeight), - saveFile); - saveButton->AddContextMenu(1); - saveButton->Tick(dt); - saveButton->SetActionCallback({ - [this, saveButton] { SelectSave(saveButton->GetSaveFile()); }, - [this, saveButton] { RenameSave(saveButton->GetSaveFile()); }, - [this, saveButton] { DeleteSave(saveButton->GetSaveFile()); } - }); + auto &saveFile = files[i]; + if(fileX == filesX) + { + fileX = 0; + fileY++; + } + ui::SaveButton * saveButton = new ui::SaveButton( + ui::Point( + buttonXOffset + buttonPadding + fileX*(buttonWidth+buttonPadding*2), + buttonYOffset + buttonPadding + fileY*(buttonHeight+buttonPadding*2) + ), + ui::Point(buttonWidth, buttonHeight), + saveFile.get()); + saveButton->AddContextMenu(1); + saveButton->Tick(dt); + saveButton->SetActionCallback({ + [this, i] { SelectSave(i); }, + [this, i] { RenameSave(i); }, + [this, i] { DeleteSave(i); } + }); - progressBar->SetStatus("Rendering thumbnails"); - progressBar->SetProgress(totalFiles ? (totalFiles - files.size()) * 100 / totalFiles : 0); - componentsQueue.push_back(saveButton); - fileX++; + progressBar->SetStatus("Rendering thumbnails"); + progressBar->SetProgress(totalFiles ? (totalFiles - files.size()) * 100 / totalFiles : 0); + componentsQueue.push_back(saveButton); + fileX++; + } } if(componentsQueue.size()) { @@ -303,8 +308,8 @@ void FileBrowserActivity::OnDraw() Graphics * g = GetGraphics(); //Window Background+Outline - g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); } FileBrowserActivity::~FileBrowserActivity() diff --git a/src/gui/filebrowser/FileBrowserActivity.h b/src/gui/filebrowser/FileBrowserActivity.h index 603fbf413..7f34b7acb 100644 --- a/src/gui/filebrowser/FileBrowserActivity.h +++ b/src/gui/filebrowser/FileBrowserActivity.h @@ -26,7 +26,8 @@ class FileBrowserActivity: public TaskListener, public WindowActivity OnSelected onSelected; ui::ScrollPanel * itemList; ui::Label * infoText; - std::vector files; + std::vector> files; + bool createButtons = false; std::vector components; std::vector componentsQueue; ByteString directory; @@ -51,9 +52,9 @@ public: void OnTryExit(ExitMethod method) override; void OnMouseDown(int x, int y, unsigned button) override; void loadDirectory(ByteString directory, ByteString search); - void SelectSave(SaveFile * file); - void DeleteSave(SaveFile * file); - void RenameSave(SaveFile * file); + void SelectSave(int index); + void DeleteSave(int index); + void RenameSave(int index); void DoSearch(ByteString search); void NotifyDone(Task * task) override; diff --git a/src/gui/font/FontEditor.cpp b/src/gui/font/FontEditor.cpp index 3db802402..b7ce036e1 100644 --- a/src/gui/font/FontEditor.cpp +++ b/src/gui/font/FontEditor.cpp @@ -1,21 +1,19 @@ +#include "FontEditor.h" +#include "bzip2/bz2wrap.h" +#include "gui/interface/Textbox.h" +#include "gui/interface/Engine.h" +#include "gui/interface/Point.h" +#include "gui/interface/Button.h" +#include "gui/interface/ScrollPanel.h" +#include "graphics/Graphics.h" +#include "SimulationConfig.h" #include #include #include #include #include - -#include "FontEditor.h" -#include "bzip2/bz2wrap.h" - -#include "Config.h" -#include "gui/interface/Textbox.h" -#include "gui/interface/Engine.h" -#include "gui/interface/Point.h" -#include "gui/interface/Button.h" -#include "gui/interface/Mouse.h" -#include "gui/interface/Keys.h" -#include "gui/interface/ScrollPanel.h" -#include "graphics/Graphics.h" +#include +#include extern unsigned char *font_data; extern unsigned int *font_ptrs; @@ -250,7 +248,7 @@ public: } }; -#define FONT_SCALE 16 +constexpr int FONT_SCALE = 16; FontEditor::FontEditor(ByteString _dataFile): ui::Window(ui::Point(0, 0), ui::Point(WINDOWW, WINDOWH)), dataFile(_dataFile), @@ -481,31 +479,31 @@ void FontEditor::OnDraw() std::array, FONT_H> const &pixels = fontPixels[currentChar]; int areaWidth = 8 + width * FONT_SCALE + 8; - g->fillrect(0, 0, areaWidth, 8 + FONT_H * FONT_SCALE + 4 + FONT_H + 4, bgR, bgG, bgB, 255); + g->DrawFilledRect(RectSized(Vec2{ 0, 0 }, Vec2{ areaWidth, 8 + FONT_H * FONT_SCALE + 4 + FONT_H + 4 }), RGB(bgR, bgG, bgB)); for(int j = 0; j < FONT_H; j++) for(int i = 0; i < width; i++) - g->fillrect(8 + i * FONT_SCALE, 8 + j * FONT_SCALE, FONT_SCALE - grid, FONT_SCALE - grid, fgR, fgG, fgB, pixels[j][i] * 255 / 3); + g->BlendFilledRect(RectSized(Vec2{ 8 + i * FONT_SCALE, 8 + j * FONT_SCALE }, Vec2{ FONT_SCALE - grid, FONT_SCALE - grid }), RGBA(fgR, fgG, fgB, pixels[j][i] * 255 / 3)); for(int j = 0; j < FONT_H; j++) for(int i = 0; i < width; i++) - g->blendpixel(8 + i, 8 + FONT_H * FONT_SCALE + 4 + j, fgR, fgG, fgB, pixels[j][i] * 255 / 3); + g->BlendPixel({ 8 + i, 8 + FONT_H * FONT_SCALE + 4 + j }, RGBA(fgR, fgG, fgB, pixels[j][i] * 255 / 3)); if(rulers) { - g->draw_line(0, 7 + 0 * FONT_SCALE , areaWidth - 1, 7 + 0 * FONT_SCALE, 128, 128, 128, 255); - g->draw_line(0, 7 + 2 * FONT_SCALE , areaWidth - 1, 7 + 2 * FONT_SCALE, 128, 128, 128, 255); - g->draw_line(0, 7 + 4 * FONT_SCALE , areaWidth - 1, 7 + 4 * FONT_SCALE, 128, 128, 128, 255); - g->draw_line(0, 7 + 9 * FONT_SCALE , areaWidth - 1, 7 + 9 * FONT_SCALE, 128, 128, 128, 255); - g->draw_line(0, 7 + 12 * FONT_SCALE , areaWidth - 1, 7 + 12 * FONT_SCALE, 128, 128, 128, 255); + g->DrawLine({ 0, 7 + 0 * FONT_SCALE }, { areaWidth - 1, 7 + 0 * FONT_SCALE }, 0x808080_rgb); + g->DrawLine({ 0, 7 + 2 * FONT_SCALE }, { areaWidth - 1, 7 + 2 * FONT_SCALE }, 0x808080_rgb); + g->DrawLine({ 0, 7 + 4 * FONT_SCALE }, { areaWidth - 1, 7 + 4 * FONT_SCALE }, 0x808080_rgb); + g->DrawLine({ 0, 7 + 9 * FONT_SCALE }, { areaWidth - 1, 7 + 9 * FONT_SCALE }, 0x808080_rgb); + g->DrawLine({ 0, 7 + 12 * FONT_SCALE }, { areaWidth - 1, 7 + 12 * FONT_SCALE }, 0x808080_rgb); - g->draw_line(7, 8, 7, 7 + FONT_H * FONT_SCALE, 128, 128, 128, 255); - g->draw_line(7 + width * FONT_SCALE, 8, 7 + width * FONT_SCALE, 7 + FONT_H * FONT_SCALE, 128, 128, 128, 255); + g->DrawLine({ 7, 8 }, { 7, 7 + FONT_H * FONT_SCALE }, 0x808080_rgb); + g->DrawLine({ 7 + width * FONT_SCALE, 8}, { 7 + width * FONT_SCALE, 7 + FONT_H * FONT_SCALE }, 0x808080_rgb); } } else { - g->drawtext(8, 8, "No character", 255, 0, 0, 255); + g->BlendText({ 8, 8 }, "No character", 0xFF0000_rgb .WithAlpha(255)); } } diff --git a/src/gui/font/FontEditor.h b/src/gui/font/FontEditor.h index 5927fbdb5..51d064f6c 100644 --- a/src/gui/font/FontEditor.h +++ b/src/gui/font/FontEditor.h @@ -1,6 +1,4 @@ -#ifndef FONTEDITOR_H -#define FONTEDITOR_H - +#pragma once #include #include #include @@ -15,7 +13,7 @@ namespace ui class Button; } -#define MAX_WIDTH 64 +constexpr int MAX_WIDTH = 64; class FontEditor: public ui::Window { private: @@ -77,5 +75,3 @@ public: void OnMouseDown(int x, int y, unsigned button) override; void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override; }; - -#endif diff --git a/src/gui/game/BitmapBrush.cpp b/src/gui/game/BitmapBrush.cpp index 93a6fe5f6..e90bf72cf 100644 --- a/src/gui/game/BitmapBrush.cpp +++ b/src/gui/game/BitmapBrush.cpp @@ -1,80 +1,68 @@ #include "BitmapBrush.h" - -#include -#include "Misc.h" - #include "common/tpt-minmax.h" +#include "Misc.h" +#include -BitmapBrush::BitmapBrush(unsigned char *newBitmap, ui::Point rectSize_): - Brush(ui::Point(0, 0)), - origSize(0, 0) +BitmapBrush::BitmapBrush(ui::Point inputSize, unsigned char const *inputBitmap) { - ui::Point newSize = rectSize_; + ui::Point newSize = inputSize; //Ensure the rect has odd dimensions so we can pull an integer radius with a 1x1 centre - if(!(newSize.X % 2)) + if (!(newSize.X % 2)) newSize.X += 1; - if(!(newSize.Y % 2)) + if (!(newSize.Y % 2)) newSize.Y += 1; - radius = (newSize-ui::Point(1, 1))/2; - size = newSize; - origSize = size; + origSize = newSize; + origBitmap = std::make_unique(newSize.X * newSize.Y); + std::fill(&origBitmap[0], &origBitmap[newSize.X * newSize.Y], 0); + for (int y = 0; y < inputSize.Y; y++) + for (int x = 0; x < inputSize.X; x++) + origBitmap[x + y * newSize.X] = inputBitmap[x + y * inputSize.X]; +} - origBitmap = new unsigned char[size.X*size.Y]; - std::fill(origBitmap, origBitmap+(size.X*size.Y), 0); - for(int y = 0; y < rectSize_.Y; y++) - { - for(int x = 0; x < rectSize_.X; x++) - { - if(newBitmap[(y*rectSize_.X)+x] >= 128) - origBitmap[(y*size.X)+x] = newBitmap[(y*rectSize_.X)+x]; - } - } - - SetRadius(radius); -}; - -void BitmapBrush::GenerateBitmap() +BitmapBrush::BitmapBrush(const BitmapBrush &other) : BitmapBrush(other.origSize, &other.origBitmap[0]) { - if(origBitmap) +} + +std::unique_ptr BitmapBrush::GenerateBitmap() const +{ + ui::Point size = radius * 2 + Vec2{ 1, 1 }; + auto bitmap = std::make_unique(size.X * size.Y); + if (size == origSize) + std::copy(&origBitmap[0], &origBitmap[origSize.X * origSize.Y], &bitmap[0]); + else { - delete[] bitmap; - bitmap = new unsigned char[size.X*size.Y]; - if(size == origSize) - std::copy(origBitmap, origBitmap+(origSize.X*origSize.Y), bitmap); - else + //Bilinear interpolation + float factorX = ((float)origSize.X)/((float)size.X); + float factorY = ((float)origSize.Y)/((float)size.Y); + for (int y = 0; y < size.Y; y++) { - //Bilinear interpolation - float factorX = ((float)origSize.X)/((float)size.X); - float factorY = ((float)origSize.Y)/((float)size.Y); - for(int y = 0; y < size.Y; y++) + for (int x = 0; x < size.X; x++) { - for(int x = 0; x < size.X; x++) - { - float originalY = ((float)y)*factorY; - float originalX = ((float)x)*factorX; + float originalY = ((float)y)*factorY; + float originalX = ((float)x)*factorX; - auto lowerX = int(std::floor(originalX)); - auto upperX = int(std::min((float)(origSize.X-1), std::floor(originalX+1.0f))); - auto lowerY = int(std::floor(originalY)); - auto upperY = int(std::min((float)(origSize.Y-1), std::floor(originalY+1.0f))); + auto lowerX = int(std::floor(originalX)); + auto upperX = int(std::min((float)(origSize.X-1), std::floor(originalX+1.0f))); + auto lowerY = int(std::floor(originalY)); + auto upperY = int(std::min((float)(origSize.Y-1), std::floor(originalY+1.0f))); - unsigned char topRight = origBitmap[(lowerY*origSize.X)+upperX]; - unsigned char topLeft = origBitmap[(lowerY*origSize.X)+lowerX]; - unsigned char bottomRight = origBitmap[(upperY*origSize.X)+upperX]; - unsigned char bottomLeft = origBitmap[(upperY*origSize.X)+lowerX]; - float top = LinearInterpolate(topLeft, topRight, float(lowerX), float(upperX), originalX); - float bottom = LinearInterpolate(bottomLeft, bottomRight, float(lowerX), float(upperX), originalX); - float mid = LinearInterpolate(top, bottom, float(lowerY), float(upperY), originalY); - bitmap[(y*size.X)+x] = mid > 128 ? 255 : 0; - } + unsigned char topRight = origBitmap[(lowerY*origSize.X)+upperX]; + unsigned char topLeft = origBitmap[(lowerY*origSize.X)+lowerX]; + unsigned char bottomRight = origBitmap[(upperY*origSize.X)+upperX]; + unsigned char bottomLeft = origBitmap[(upperY*origSize.X)+lowerX]; + float top = LinearInterpolate(topLeft, topRight, float(lowerX), float(upperX), originalX); + float bottom = LinearInterpolate(bottomLeft, bottomRight, float(lowerX), float(upperX), originalX); + float mid = LinearInterpolate(top, bottom, float(lowerY), float(upperY), originalY); + bitmap[(y*size.X)+x] = mid > 128 ? 255 : 0; } } } + return bitmap; } -BitmapBrush::~BitmapBrush() +std::unique_ptr BitmapBrush::Clone() const { - delete[] origBitmap; + return std::make_unique(*this); } diff --git a/src/gui/game/BitmapBrush.h b/src/gui/game/BitmapBrush.h index c6a7f0de2..3a087b7b0 100644 --- a/src/gui/game/BitmapBrush.h +++ b/src/gui/game/BitmapBrush.h @@ -1,25 +1,18 @@ -/* - * BitmapBrush.h - * - * Created on: Nov 18, 2012 - * Author: Simon Robertshaw - */ - -#ifndef BTIMAPBRUSH_H_ -#define BTIMAPBRUSH_H_ - +#pragma once #include #include "Brush.h" class BitmapBrush: public Brush { -protected: - ui::Point origSize; - unsigned char * origBitmap; -public: - BitmapBrush(unsigned char *newBitmap, ui::Point rectSize); - void GenerateBitmap() override; - virtual ~BitmapBrush(); -}; + ui::Point origSize{ 0, 0 }; + // 2D array with coords [0, origSize.X) by [0, origSize.Y) + std::unique_ptr origBitmap; -#endif /* BTIMAPBRUSH_H_ */ +public: + BitmapBrush(ui::Point size, unsigned char const *bitmap); + BitmapBrush(const BitmapBrush &other); + virtual ~BitmapBrush() override = default; + std::unique_ptr GenerateBitmap() const override; + + std::unique_ptr Clone() const override; +}; diff --git a/src/gui/game/Brush.cpp b/src/gui/game/Brush.cpp index c14a1eb78..e73b53f4e 100644 --- a/src/gui/game/Brush.cpp +++ b/src/gui/game/Brush.cpp @@ -1,126 +1,134 @@ #include "Brush.h" #include "graphics/Renderer.h" -Brush::Brush(ui::Point size_): - outline(NULL), - bitmap(NULL), - size(0, 0), - radius(0, 0) +Brush::Brush(const Brush &other) { - SetRadius(size_); -}; - -Brush::~Brush() -{ - delete[] bitmap; - delete[] outline; + radius = other.radius; + auto size = GetSize(); + if (other.bitmap) + { + bitmap = std::make_unique(size.X * size.Y); + std::copy(&other.bitmap[0], &other.bitmap[0] + size.X * size.Y, &bitmap[0]); + } + if (other.outline) + { + outline = std::make_unique(size.X * size.Y); + std::copy(&other.outline[0], &other.outline[0] + size.X * size.Y, &outline[0]); + } } -void Brush::updateOutline() +void Brush::InitBitmap() { - if(!bitmap) - GenerateBitmap(); - if(!bitmap) - return; - delete[] outline; - outline = new unsigned char[size.X*size.Y]; - for(int x = 0; x < size.X; x++) + bitmap = GenerateBitmap(); +} + +void Brush::InitOutline() +{ + InitBitmap(); + ui::Point bounds = GetSize(); + outline = std::make_unique(bounds.X * bounds.Y); + for (int j = 0; j < bounds.Y; j++) { - for(int y = 0; y < size.Y; y++) + for (int i = 0; i < bounds.X; i++) { - if(bitmap[y*size.X+x] && (!y || !x || x == size.X-1 || y == size.Y-1 || !bitmap[y*size.X+(x+1)] || !bitmap[y*size.X+(x-1)] || !bitmap[(y-1)*size.X+x] || !bitmap[(y+1)*size.X+x])) + bool value = false; + if (bitmap[i + j * bounds.X]) { - outline[y*size.X+x] = 255; + if (i == 0 || j == 0 || i == bounds.X - 1 || j == bounds.Y - 1) + value = true; + else if (!bitmap[(i + 1) + j * bounds.X]) + value = true; + else if (!bitmap[(i - 1) + j * bounds.X]) + value = true; + else if (!bitmap[i + (j + 1) * bounds.X]) + value = true; + else if (!bitmap[i + (j - 1) * bounds.X]) + value = true; } - else - outline[y*size.X+x] = 0; + outline[i + j * bounds.X] = value ? 0xFF : 0; } } } -void Brush::SetRadius(ui::Point radius) +void Brush::SetRadius(ui::Point newRadius) { - this->radius = radius; - this->size = radius+radius+ui::Point(1, 1); - - GenerateBitmap(); - updateOutline(); + radius = newRadius; + InitOutline(); } -void Brush::GenerateBitmap() +void Brush::AdjustSize(int delta, bool logarithmic, bool keepX, bool keepY) { - delete[] bitmap; - bitmap = new unsigned char[size.X*size.Y]; - for(int x = 0; x < size.X; x++) - { - for(int y = 0; y < size.Y; y++) - { - bitmap[(y*size.X)+x] = 255; - } - } + if (keepX && keepY) + return; + + ui::Point newSize(0, 0); + ui::Point oldSize = GetRadius(); + if (logarithmic) + 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)); + else if (keepX) + SetRadius(ui::Point(oldSize.X, newSize.Y)); + else + SetRadius(newSize); } -unsigned char *Brush::GetBitmap() -{ - if(!bitmap) - GenerateBitmap(); - return bitmap; -} - -unsigned char *Brush::GetOutline() -{ - if(!outline) - updateOutline(); - if(!outline) - return NULL; - return outline; -} - -void Brush::RenderRect(Renderer * ren, ui::Point position1, ui::Point position2) +void Brush::RenderRect(Renderer * ren, ui::Point position1, ui::Point position2) const { int width, height; width = position2.X-position1.X; height = position2.Y-position1.Y; - if(height<0) + if (height<0) { position1.Y += height; height *= -1; } - if(width<0) + if (width<0) { position1.X += width; width *= -1; } - ren->xor_line(position1.X, position1.Y, position1.X+width, position1.Y); - if(height>0){ - ren->xor_line(position1.X, position1.Y+height, position1.X+width, position1.Y+height); - if(height>1){ - ren->xor_line(position1.X+width, position1.Y+1, position1.X+width, position1.Y+height-1); - if(width>0) - ren->xor_line(position1.X, position1.Y+1, position1.X, position1.Y+height-1); + ren->XorLine(position1, position1 + Vec2{ width, 0 }); + if (height > 0) + { + ren->XorLine(position1 + Vec2{ 0, height }, position1 + Vec2{ width, height }); + if (height > 1) + { + ren->XorLine(position1 + Vec2{ width, 1 }, position1 + Vec2{ width, height - 1 }); + if (width > 0) + { + ren->XorLine(position1 + Vec2{ 0, 1 }, position1 + Vec2{ 0, height - 1 }); + } } } } -void Brush::RenderLine(Renderer * ren, ui::Point position1, ui::Point position2) +void Brush::RenderLine(Renderer * ren, ui::Point position1, ui::Point position2) const { - ren->xor_line(position1.X, position1.Y, position2.X, position2.Y); + ren->XorLine(position1, position2); } -void Brush::RenderPoint(Renderer * ren, ui::Point position) +void Brush::RenderPoint(Renderer * ren, ui::Point position) const { - if(!outline) - updateOutline(); - if(!outline) - return; - ren->xor_bitmap(outline, position.X-radius.X, position.Y-radius.Y, size.X, size.Y); + ren->XorImage(&outline[0], RectBetween(position - radius, position + radius)); } -void Brush::RenderFill(Renderer * ren, ui::Point position) +void Brush::RenderFill(Renderer * ren, ui::Point position) const { - ren->xor_line(position.X-5, position.Y, position.X-1, position.Y); - ren->xor_line(position.X+5, position.Y, position.X+1, position.Y); - ren->xor_line(position.X, position.Y-5, position.X, position.Y-1); - ren->xor_line(position.X, position.Y+5, position.X, position.Y+1); + ren->XorLine(position - Vec2{ 5, 0 }, position - Vec2{ 1, 0 }); + ren->XorLine(position + Vec2{ 5, 0 }, position + Vec2{ 1, 0 }); + ren->XorLine(position - Vec2{ 0, 5 }, position - Vec2{ 0, 1 }); + ren->XorLine(position + Vec2{ 0, 5 }, position + Vec2{ 0, 1 }); } diff --git a/src/gui/game/Brush.h b/src/gui/game/Brush.h index aa9b2b79d..48a5954a2 100644 --- a/src/gui/game/Brush.h +++ b/src/gui/game/Brush.h @@ -1,44 +1,91 @@ -#ifndef BRUSH_H_ -#define BRUSH_H_ -#include "Config.h" - +#pragma once #include "gui/interface/Point.h" +#include class Renderer; class Brush { -protected: - unsigned char * outline; - unsigned char * bitmap; - ui::Point size; - ui::Point radius; - void updateOutline(); -public: - Brush(ui::Point size); +private: + // 2D arrays indexed by coordinates from [-radius.X, radius.X] by [-radius.Y, radius.Y] + std::unique_ptr bitmap; + std::unique_ptr outline; - //Radius of the brush 0x0 - infxinf (Radius of 0x0 would be 1x1, radius of 1x1 would be 3x3) - ui::Point GetRadius() + void InitBitmap(); + void InitOutline(); + + struct iterator + { + Brush const &parent; + int x, y; + + iterator &operator++() + { + auto radius = parent.GetRadius(); + do + { + if (++x > radius.X) + { + --y; + x = -radius.X; + } + } while (y >= -radius.Y && !parent.bitmap[x + radius.X + (y + radius.Y) * (2 * radius.X + 1)]); + return *this; + } + + ui::Point operator*() const + { + return ui::Point(x, y); + } + + bool operator!=(iterator other) const + { + return x != other.x || y != other.y; + } + + using difference_type = void; + using value_type = ui::Point; + using pointer = void; + using reference = void; + using iterator_category = std::forward_iterator_tag; + }; + +protected: + ui::Point radius{ 0, 0 }; + + virtual std::unique_ptr GenerateBitmap() const = 0; + +public: + Brush() = default; + Brush(const Brush &other); + virtual ~Brush() = default; + virtual void AdjustSize(int delta, bool logarithmic, bool keepX, bool keepY); + virtual std::unique_ptr Clone() const = 0; + + ui::Point GetSize() const + { + return radius * 2 + Vec2{ 1, 1 }; + } + + ui::Point GetRadius() const { return radius; } - //Size of the brush bitmap mask, 1x1 - infxinf - ui::Point GetSize() + iterator begin() const { - return size; + // bottom to top is the preferred order for Simulation::CreateParts + return ++iterator{*this, radius.X, radius.Y + 1}; } - virtual void SetRadius(ui::Point radius); - virtual ~Brush(); - virtual void RenderRect(Renderer * ren, ui::Point position1, ui::Point position2); - virtual void RenderLine(Renderer * ren, ui::Point position1, ui::Point position2); - virtual void RenderPoint(Renderer * ren, ui::Point position); - virtual void RenderFill(Renderer * ren, ui::Point position); - virtual void GenerateBitmap(); - //Get a bitmap for drawing particles - unsigned char * GetBitmap(); - unsigned char * GetOutline(); + iterator end() const + { + return iterator{*this, -radius.X, -radius.Y - 1}; + } + + void RenderRect(Renderer * ren, ui::Point position1, ui::Point position2) const; + void RenderLine(Renderer * ren, ui::Point position1, ui::Point position2) const; + void RenderPoint(Renderer * ren, ui::Point position) const; + void RenderFill(Renderer * ren, ui::Point position) const; + + void SetRadius(ui::Point newRadius); }; - - -#endif /* BRUSH_H_ */ diff --git a/src/gui/game/DecorationTool.cpp b/src/gui/game/DecorationTool.cpp index 1e4b9ef85..3ac55acac 100644 --- a/src/gui/game/DecorationTool.cpp +++ b/src/gui/game/DecorationTool.cpp @@ -5,84 +5,60 @@ #include "simulation/SimulationData.h" #include "simulation/Simulation.h" -VideoBuffer *DecorationTool::GetIcon(int toolID, int width, int height) +std::unique_ptr DecorationTool::GetIcon(int ToolID, Vec2 size) { - VideoBuffer * newTexture = new VideoBuffer(width, height); - for (int y=0; ySetPixel(x, y, 0, 255-5*x, 5*x, 255); - else if (toolID == DECO_DRAW || toolID == DECO_CLEAR) - newTexture->SetPixel(x, y, Red, Green, Blue, Alpha); - else - newTexture->SetPixel(x, y, 50, 50, 50, 255); - } - } - if (toolID == DECO_CLEAR) - { - int reverseRed = (Red+127)%256; - int reverseGreen = (Green+127)%256; - int reverseBlue = (Blue+127)%256; - for (int y=4; y<12; y++) - { - newTexture->SetPixel(y+5, y-1, reverseRed, reverseGreen, reverseBlue, 255); - newTexture->SetPixel(y+6, y-1, reverseRed, reverseGreen, reverseBlue, 255); - newTexture->SetPixel(20-y, y-1, reverseRed, reverseGreen, reverseBlue, 255); - newTexture->SetPixel(21-y, y-1, reverseRed, reverseGreen, reverseBlue, 255); - } - } - else if (toolID == DECO_ADD) - newTexture->AddCharacter(11, 4, '+', Red, Green, Blue, 255); - else if (toolID == DECO_SUBTRACT) - newTexture->AddCharacter(11, 4, '-', Red, Green, Blue, 255); - else if (toolID == DECO_MULTIPLY) - newTexture->AddCharacter(11, 3, 'x', Red, Green, Blue, 255); - else if (toolID == DECO_DIVIDE) - newTexture->AddCharacter(11, 4, '/', Red, Green, Blue, 255); - return newTexture; -} + auto texture = std::make_unique(size); -DecorationTool::DecorationTool(Renderer *ren_, int decoMode, String name, String description, int r, int g, int b, ByteString identifier): - Tool(decoMode, name, description, r, g, b, identifier), - Red(0), - Green(0), - Blue(0), - Alpha(0), - ren(ren_) -{ -} - -DecorationTool::~DecorationTool() -{ -} - -void DecorationTool::Draw(Simulation * sim, Brush * brush, ui::Point position) -{ - sim->ApplyDecorationPoint(position.X, position.Y, Red, Green, Blue, Alpha, toolID, brush); -} - -void DecorationTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) -{ - sim->ApplyDecorationLine(position1.X, position1.Y, position2.X, position2.Y, Red, Green, Blue, Alpha, toolID, brush); -} - -void DecorationTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) -{ - sim->ApplyDecorationBox(position1.X, position1.Y, position2.X, position2.Y, Red, Green, Blue, Alpha, toolID); -} - -void DecorationTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) -{ - pixel loc = ren->vid[position.X+position.Y*WINDOWW]; - if (toolID == DECO_CLEAR) - sim->ApplyDecorationFill(ren, position.X, position.Y, 0, 0, 0, 0, PIXR(loc), PIXG(loc), PIXB(loc)); + if (ToolID == DECO_SMUDGE) + for (auto pos : size.OriginRect()) + texture->DrawPixel(pos, RGB(0, 0xFF - 5 * pos.X, 5 * pos.X)); + else if (ToolID == DECO_DRAW || ToolID == DECO_CLEAR) + texture->BlendFilledRect(size.OriginRect(), Colour); else - sim->ApplyDecorationFill(ren, position.X, position.Y, Red, Green, Blue, Alpha, PIXR(loc), PIXG(loc), PIXB(loc)); + texture->DrawFilledRect(size.OriginRect(), 0x323232_rgb); + + if (ToolID == DECO_CLEAR) + { + auto reverse = RGB(Colour.Red + 127, Colour.Green + 127, Colour.Blue + 127).WithAlpha(0xFF); + texture->BlendChar(size / 2 - Vec2(4, 2), 0xE06C, reverse); + } + else + { + auto colour = Colour.NoAlpha().WithAlpha(0xFF); + if (ToolID == DECO_ADD) + texture->AddChar(Vec2(11, 4), '+', colour); + else if (ToolID == DECO_SUBTRACT) + texture->AddChar(Vec2(11, 4), '-', colour); + else if (ToolID == DECO_MULTIPLY) + texture->AddChar(Vec2(11, 3), 'x', colour); + else if (ToolID == DECO_DIVIDE) + texture->AddChar(Vec2(11, 4), '/', colour); + } + return texture; +} + +void DecorationTool::Draw(Simulation * sim, Brush const &brush, ui::Point position) +{ + sim->ApplyDecorationPoint(position.X, position.Y, Colour.Red, Colour.Green, Colour.Blue, Colour.Alpha, ToolID, brush); +} + +void DecorationTool::DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging) +{ + sim->ApplyDecorationLine(position1.X, position1.Y, position2.X, position2.Y, Colour.Red, Colour.Green, Colour.Blue, Colour.Alpha, ToolID, brush); +} + +void DecorationTool::DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) +{ + sim->ApplyDecorationBox(position1.X, position1.Y, position2.X, position2.Y, Colour.Red, Colour.Green, Colour.Blue, Colour.Alpha, ToolID); +} + +void DecorationTool::DrawFill(Simulation * sim, Brush const &brush, ui::Point position) +{ + auto loc = RGB::Unpack(ren.GetPixel(position)); + if (ToolID == DECO_CLEAR) + // TODO: this is actually const-correct + sim->ApplyDecorationFill(const_cast(&ren), position.X, position.Y, 0, 0, 0, 0, loc.Red, loc.Green, loc.Blue); + else + sim->ApplyDecorationFill(const_cast(&ren), position.X, position.Y, Colour.Red, Colour.Green, Colour.Blue, Colour.Alpha, loc.Red, loc.Green, loc.Blue); } diff --git a/src/gui/game/DecorationTool.h b/src/gui/game/DecorationTool.h index d194e93d3..805b16224 100644 --- a/src/gui/game/DecorationTool.h +++ b/src/gui/game/DecorationTool.h @@ -1,6 +1,5 @@ -#ifndef DECORATIONTOOL_H_ -#define DECORATIONTOOL_H_ - +#pragma once +#include #include "Tool.h" #include "graphics/Graphics.h" @@ -8,20 +7,22 @@ class Renderer; class DecorationTool: public Tool { public: - unsigned char Red; - unsigned char Green; - unsigned char Blue; - unsigned char Alpha; - Renderer *ren; + RGBA Colour; + Renderer const &ren; - VideoBuffer * GetIcon(int toolID, int width, int height); + std::unique_ptr GetIcon(int toolID, Vec2 size); - DecorationTool(Renderer *ren_, int decoMode, String name, String description, int r, int g, int b, ByteString identifier); - virtual ~DecorationTool(); - void Draw(Simulation * sim, Brush * brush, ui::Point position) override; - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) override; - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override; - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override; + DecorationTool(Renderer const &ren, int decoMode, String name, String description, RGB colour, ByteString identifier): + Tool(decoMode, name, description, colour, identifier), + Colour(0x000000_rgb .WithAlpha(0x00)), + ren(ren) + {} + + virtual ~DecorationTool() + {} + + void Draw(Simulation * sim, Brush const &brush, ui::Point position) override; + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging) override; + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override; + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override; }; - -#endif diff --git a/src/gui/game/EllipseBrush.h b/src/gui/game/EllipseBrush.h index 0c0569192..fca39f7c0 100644 --- a/src/gui/game/EllipseBrush.h +++ b/src/gui/game/EllipseBrush.h @@ -1,25 +1,23 @@ -#ifndef ELIPSEBRUSH_H_ -#define ELIPSEBRUSH_H_ - -#include +#pragma once #include "Brush.h" +#include class EllipseBrush: public Brush { bool perfectCircle; public: - EllipseBrush(ui::Point size, bool perfectCircle = true): - Brush(size) + EllipseBrush(bool newPerfectCircle) : + perfectCircle(newPerfectCircle) { - this->perfectCircle = perfectCircle; - SetRadius(size); } + virtual ~EllipseBrush() override = default; - void GenerateBitmap() override + std::unique_ptr GenerateBitmap() const override { - delete[] bitmap; - bitmap = new unsigned char[size.X*size.Y]; + ui::Point size = radius * 2 + Vec2{ 1, 1 }; + auto bitmap = std::make_unique(size.X * size.Y); + int rx = radius.X; int ry = radius.Y; @@ -63,7 +61,11 @@ public: bitmap[size.X/2] = 255; bitmap[size.X*size.Y-size.X/2-1] = 255; } + return bitmap; + } + + std::unique_ptr Clone() const override + { + return std::make_unique(*this); } }; - -#endif /* ELIPSEBRUSH_H_ */ diff --git a/src/gui/game/Favorite.cpp b/src/gui/game/Favorite.cpp index 36b73ccb5..17b4e0ee6 100644 --- a/src/gui/game/Favorite.cpp +++ b/src/gui/game/Favorite.cpp @@ -1,8 +1,6 @@ #include "Favorite.h" - +#include "prefs/GlobalPrefs.h" #include -#include "client/Client.h" - #include Favorite::Favorite(): @@ -42,10 +40,10 @@ void Favorite::RemoveFavorite(ByteString identifier) void Favorite::SaveFavoritesToPrefs() { - Client::Ref().SetPref("Favorites", std::vector(favoritesList.begin(), favoritesList.end())); + GlobalPrefs::Ref().Set("Favorites", favoritesList); } void Favorite::LoadFavoritesFromPrefs() { - favoritesList = Client::Ref().GetPrefByteStringArray("Favorites"); + favoritesList = GlobalPrefs::Ref().Get("Favorites", std::vector{}); } diff --git a/src/gui/game/Favorite.h b/src/gui/game/Favorite.h index 5bf87ddce..244c63348 100644 --- a/src/gui/game/Favorite.h +++ b/src/gui/game/Favorite.h @@ -1,13 +1,9 @@ -#ifndef FAVORITE_H -#define FAVORITE_H -#include "Config.h" - +#pragma once #include "common/String.h" +#include "common/ExplicitSingleton.h" #include -#include "common/Singleton.h" - -class Favorite : public Singleton +class Favorite : public ExplicitSingleton { std::vector favoritesList; public: @@ -23,4 +19,3 @@ public: void SaveFavoritesToPrefs(); void LoadFavoritesFromPrefs(); }; -#endif //FAVORITE_H diff --git a/src/gui/game/GOLTool.cpp b/src/gui/game/GOLTool.cpp index 211bfc8d1..19381f19c 100644 --- a/src/gui/game/GOLTool.cpp +++ b/src/gui/game/GOLTool.cpp @@ -1,5 +1,6 @@ #include "Tool.h" +#include "prefs/GlobalPrefs.h" #include "client/Client.h" #include "common/tpt-rand.h" #include "simulation/GOLString.h" @@ -18,28 +19,36 @@ class GOLWindow: public ui::Window { - void UpdateGradient(); - -public: ui::Colour highColour, lowColour; ui::Button *highColourButton, *lowColourButton; ui::Textbox *nameField, *ruleField; - GameModel * gameModel; + GameModel &gameModel; Simulation *sim; int toolSelection; - GOLWindow(GameModel *gameModel, Simulation *sim, int toolSelection, int rule, int colour1, int colour2); - void Validate(); + + void updateGradient(); + void validate(); + +public: + GOLWindow(GameModel &gameModel, Simulation *sim, int toolSelection, int rule, RGB colour1, RGB colour2); + + virtual ~GOLWindow() + {} + void OnDraw() override; void OnTryExit(ExitMethod method) override; - virtual ~GOLWindow() {} }; -GOLWindow::GOLWindow(GameModel * gameModel_, Simulation *sim_, int toolSelection, int rule, int colour1, int colour2): -ui::Window(ui::Point(-1, -1), ui::Point(200, 108)), -gameModel(gameModel_), -sim(sim_), -toolSelection(toolSelection) +GOLWindow::GOLWindow(GameModel &gameModel_, Simulation *sim_, int toolSelection, int rule, RGB colour1, RGB colour2): + ui::Window(ui::Point(-1, -1), ui::Point(200, 108)), + highColour(colour1.WithAlpha(0xFF)), + lowColour(colour2.WithAlpha(0xFF)), + gameModel(gameModel_), + sim(sim_), + toolSelection(toolSelection) { + highColour.Alpha = 255; + lowColour.Alpha = 255; ui::Label * messageLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 14), "Edit custom GOL type"); messageLabel->SetTextColour(style::Colour::InformationTitle); messageLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; @@ -54,7 +63,7 @@ toolSelection(toolSelection) if (nameField->GetText().length() && ruleField->GetText().length()) { CloseActiveWindow(); - Validate(); + validate(); SelfDestruct(); } } }); @@ -78,7 +87,7 @@ toolSelection(toolSelection) highColourButton->SetActionCallback({ [this] { new ColourPickerActivity(highColour, [this](ui::Colour colour) { highColour = colour; - UpdateGradient(); + updateGradient(); }); } }); AddComponent(highColourButton); @@ -87,7 +96,7 @@ toolSelection(toolSelection) lowColourButton->SetActionCallback({ [this] { new ColourPickerActivity(lowColour, [this](ui::Colour colour) { lowColour = colour; - UpdateGradient(); + updateGradient(); }); } }); AddComponent(lowColourButton); @@ -96,32 +105,27 @@ toolSelection(toolSelection) { ruleField->SetText(SerialiseGOLRule(rule)); nameField->SetText(""); - highColour.Red = PIXR(colour1); - highColour.Green = PIXG(colour1); - highColour.Blue = PIXB(colour1); - lowColour.Red = PIXR(colour2); - lowColour.Green = PIXG(colour2); - lowColour.Blue = PIXB(colour2); } else { - ruleField->SetText(Client::Ref().GetPrefString("CustomGOL.Rule", "B3/S23")); - nameField->SetText(Client::Ref().GetPrefString("CustomGOL.Name", "CGOL")); - highColour.Red = RNG::Ref().between(0x80, 0xFF); - highColour.Green = RNG::Ref().between(0x80, 0xFF); - highColour.Blue = RNG::Ref().between(0x80, 0xFF); - lowColour.Red = RNG::Ref().between(0x00, 0x7F); - lowColour.Green = RNG::Ref().between(0x00, 0x7F); - lowColour.Blue = RNG::Ref().between(0x00, 0x7F); + auto &prefs = GlobalPrefs::Ref(); + ruleField->SetText(prefs.Get("CustomGOL.Rule", String("B3/S23"))); + nameField->SetText(prefs.Get("CustomGOL.Name", String("CGOL"))); + highColour.Red = interfaceRng.between(0x80, 0xFF); + highColour.Green = interfaceRng.between(0x80, 0xFF); + highColour.Blue = interfaceRng.between(0x80, 0xFF); + highColour.Alpha = 0xFF; + lowColour.Red = interfaceRng.between(0x00, 0x7F); + lowColour.Green = interfaceRng.between(0x00, 0x7F); + lowColour.Blue = interfaceRng.between(0x00, 0x7F); + lowColour.Alpha = 0xFF; } - highColour.Alpha = 255; - lowColour.Alpha = 255; - UpdateGradient(); + updateGradient(); MakeActiveWindow(); } -void GOLWindow::UpdateGradient() +void GOLWindow::updateGradient() { highColourButton->Appearance.BackgroundInactive = highColour; highColourButton->Appearance.BackgroundHover = highColour; @@ -129,7 +133,7 @@ void GOLWindow::UpdateGradient() lowColourButton->Appearance.BackgroundHover = lowColour; } -void GOLWindow::Validate() +void GOLWindow::validate() { auto nameString = nameField->GetText(); auto ruleString = ruleField->GetText(); @@ -152,8 +156,12 @@ void GOLWindow::Validate() } ruleString = SerialiseGOLRule(rule); // * Make it canonical. - Client::Ref().SetPrefUnicode("CustomGOL.Name", nameString); - Client::Ref().SetPrefUnicode("CustomGOL.Rule", ruleString); + { + auto &prefs = GlobalPrefs::Ref(); + Prefs::DeferWrite dw(prefs); + prefs.Set("CustomGOL.Name", nameString); + prefs.Set("CustomGOL.Rule", ruleString); + } auto color1 = (((highColour.Red << 8) | highColour.Green) << 8) | highColour.Blue; auto color2 = (((lowColour.Red << 8) | lowColour.Green) << 8) | lowColour.Blue; @@ -163,8 +171,8 @@ void GOLWindow::Validate() return; } - gameModel->SelectNextIdentifier = "DEFAULT_PT_LIFECUST_" + nameString.ToAscii(); - gameModel->SelectNextTool = toolSelection; + gameModel.SelectNextIdentifier = "DEFAULT_PT_LIFECUST_" + nameString.ToAscii(); + gameModel.SelectNextTool = toolSelection; } void GOLWindow::OnTryExit(ExitMethod method) @@ -177,8 +185,8 @@ void GOLWindow::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); int width = Size.X - 60; for (int xx = 0; xx < width; ++xx) @@ -189,12 +197,12 @@ void GOLWindow::OnDraw() auto rr = int(highColour.Red * (1.f - f) + lowColour.Red * f); auto gg = int(highColour.Green * (1.f - f) + lowColour.Green * f); auto bb = int(highColour.Blue * (1.f - f) + lowColour.Blue * f); - g->blendpixel(Position.X + xx + 30, Position.Y + yy + 67, rr, gg, bb, 255); + g->DrawPixel(Position + Vec2{ xx + 30, yy + 67 }, RGB(rr, gg, bb)); } } } -void GOLTool::OpenWindow(Simulation *sim, int toolSelection, int rule, int colour1, int colour2) +void GOLTool::OpenWindow(Simulation *sim, int toolSelection, int rule, RGB colour1, RGB colour2) { new GOLWindow(gameModel, sim, toolSelection, rule, colour1, colour2); } diff --git a/src/gui/game/GameController.cpp b/src/gui/game/GameController.cpp index 7367a728c..c9c962db6 100644 --- a/src/gui/game/GameController.cpp +++ b/src/gui/game/GameController.cpp @@ -1,7 +1,6 @@ #include "GameController.h" #include "Brush.h" -#include "Config.h" #include "Controller.h" #include "Format.h" #include "GameModel.h" @@ -13,16 +12,13 @@ #include "RenderPreset.h" #include "Tool.h" -#ifdef LUACONSOLE -# include "lua/LuaScriptInterface.h" -# include "lua/LuaEvents.h" -#else -# include "lua/TPTScriptInterface.h" -#endif +#include "GameControllerEvents.h" +#include "lua/CommandInterface.h" +#include "prefs/GlobalPrefs.h" #include "client/Client.h" #include "client/GameSave.h" -#include "common/Platform.h" +#include "common/platform/Platform.h" #include "debug/DebugInfo.h" #include "debug/DebugLines.h" #include "debug/DebugParts.h" @@ -38,8 +34,6 @@ #include "gui/dialogues/ErrorMessage.h" #include "gui/dialogues/InformationMessage.h" #include "gui/dialogues/ConfirmPrompt.h" -#include "gui/interface/Keys.h" -#include "gui/interface/Mouse.h" #include "gui/interface/Engine.h" #include "gui/colourpicker/ColourPickerActivity.h" @@ -67,6 +61,10 @@ #include "gui/tags/TagsController.h" #include "gui/tags/TagsView.h" +#include "Config.h" +#include +#include + #ifdef GetUserName # undef GetUserName // dammit windows #endif @@ -92,13 +90,9 @@ GameController::GameController(): gameView->AttachController(this); gameModel->AddObserver(gameView); - gameView->SetDebugHUD(Client::Ref().GetPrefBool("Renderer.DebugMode", false)); + gameView->SetDebugHUD(GlobalPrefs::Ref().Get("Renderer.DebugMode", false)); -#ifdef LUACONSOLE - commandInterface = new LuaScriptInterface(this, gameModel); -#else - commandInterface = new TPTScriptInterface(this, gameModel); -#endif + CommandInterface::Create(this, gameModel); Client::Ref().AddListener(this); @@ -245,36 +239,36 @@ std::pair GameController::GetSignSplit(int signID) void GameController::PlaceSave(ui::Point position) { - GameSave *placeSave = gameModel->GetPlaceSave(); + auto *placeSave = gameModel->GetTransformedPlaceSave(); if (placeSave) { HistorySnapshot(); - if (!gameModel->GetSimulation()->Load(placeSave, !gameView->ShiftBehaviour(), position.X, position.Y)) - { - gameModel->SetPaused(placeSave->paused | gameModel->GetPaused()); - Client::Ref().MergeStampAuthorInfo(placeSave->authors); - } + gameModel->GetSimulation()->Load(placeSave, !gameView->ShiftBehaviour(), position); + gameModel->SetPaused(placeSave->paused | gameModel->GetPaused()); + Client::Ref().MergeStampAuthorInfo(placeSave->authors); } + gameModel->SetPlaceSave(nullptr); } void GameController::Install() { -#if defined(MACOSX) - new InformationMessage("No installation necessary", "You don't need to install " APPNAME " on OS X", false); -#elif defined(WIN) || defined(LIN) - new ConfirmPrompt("Install " APPNAME, "Do you wish to install " APPNAME " on this computer?\nThis allows you to open save files and saves directly from the website.", { [] { - if (Client::Ref().DoInstallation()) - { - new InformationMessage("Success", "Installation completed", false); - } - else - { - new ErrorMessage("Could not install", "The installation did not complete due to an error"); - } - } }); -#else - new ErrorMessage("Cannot install", "You cannot install " APPNAME " on this platform"); -#endif + if constexpr (CAN_INSTALL) + { + new ConfirmPrompt("Install " + String(APPNAME), "Do you wish to install " + String(APPNAME) + " on this computer?\nThis allows you to open save files and saves directly from the website.", { [] { + if (Platform::Install()) + { + new InformationMessage("Success", "Installation completed", false); + } + else + { + new ErrorMessage("Could not install", "The installation did not complete due to an error"); + } + } }); + } + else + { + new InformationMessage("No installation necessary", "You don't need to install " + String(APPNAME) + " on this platform", false); + } } void GameController::AdjustGridSize(int direction) @@ -291,37 +285,14 @@ void GameController::InvertAirSim() } -void GameController::AdjustBrushSize(int delta, bool logarithmic, bool xAxis, bool yAxis) +void GameController::AdjustBrushSize(int delta, bool logarithmic, bool keepX, bool keepY) { - if(xAxis && yAxis) - return; - - ui::Point newSize(0, 0); - ui::Point oldSize = gameModel->GetBrush()->GetRadius(); - if(logarithmic) - newSize = gameModel->GetBrush()->GetRadius() + ui::Point(delta * std::max(gameModel->GetBrush()->GetRadius().X / 5, 1), delta * std::max(gameModel->GetBrush()->GetRadius().Y / 5, 1)); - else - newSize = gameModel->GetBrush()->GetRadius() + 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(xAxis) - SetBrushSize(ui::Point(newSize.X, oldSize.Y)); - else if(yAxis) - SetBrushSize(ui::Point(oldSize.X, newSize.Y)); - else - SetBrushSize(newSize); + gameModel->GetBrush().AdjustSize(delta, logarithmic, keepX, keepY); } void GameController::SetBrushSize(ui::Point newSize) { - gameModel->GetBrush()->SetRadius(newSize); + gameModel->GetBrush().SetRadius(newSize); } void GameController::AdjustZoomSize(int delta, bool logarithmic) @@ -371,6 +342,11 @@ ui::Point GameController::PointTranslate(ui::Point point) return gameModel->AdjustZoomCoords(point); } +ui::Point GameController::PointTranslateNoClamp(ui::Point point) +{ + return gameModel->AdjustZoomCoords(point); +} + ui::Point GameController::NormaliseBlockCoord(ui::Point point) { return (point/CELL)*CELL; @@ -381,10 +357,10 @@ void GameController::DrawRect(int toolSelection, ui::Point point1, ui::Point poi Simulation * sim = gameModel->GetSimulation(); Tool * activeTool = gameModel->GetActiveTool(toolSelection); gameModel->SetLastTool(activeTool); - Brush * cBrush = gameModel->GetBrush(); - if(!activeTool || !cBrush) + Brush &cBrush = gameModel->GetBrush(); + if (!activeTool) return; - activeTool->SetStrength(1.0f); + activeTool->Strength = 1.0f; activeTool->DrawRect(sim, cBrush, point1, point2); } @@ -393,10 +369,10 @@ void GameController::DrawLine(int toolSelection, ui::Point point1, ui::Point poi Simulation * sim = gameModel->GetSimulation(); Tool * activeTool = gameModel->GetActiveTool(toolSelection); gameModel->SetLastTool(activeTool); - Brush * cBrush = gameModel->GetBrush(); - if(!activeTool || !cBrush) + Brush &cBrush = gameModel->GetBrush(); + if (!activeTool) return; - activeTool->SetStrength(1.0f); + activeTool->Strength = 1.0f; activeTool->DrawLine(sim, cBrush, point1, point2); } @@ -405,10 +381,10 @@ void GameController::DrawFill(int toolSelection, ui::Point point) Simulation * sim = gameModel->GetSimulation(); Tool * activeTool = gameModel->GetActiveTool(toolSelection); gameModel->SetLastTool(activeTool); - Brush * cBrush = gameModel->GetBrush(); - if(!activeTool || !cBrush) + Brush &cBrush = gameModel->GetBrush(); + if (!activeTool) return; - activeTool->SetStrength(1.0f); + activeTool->Strength = 1.0f; activeTool->DrawFill(sim, cBrush, point); } @@ -417,13 +393,13 @@ void GameController::DrawPoints(int toolSelection, ui::Point oldPos, ui::Point n Simulation * sim = gameModel->GetSimulation(); Tool * activeTool = gameModel->GetActiveTool(toolSelection); gameModel->SetLastTool(activeTool); - Brush * cBrush = gameModel->GetBrush(); - if (!activeTool || !cBrush) + Brush &cBrush = gameModel->GetBrush(); + if (!activeTool) { return; } - activeTool->SetStrength(gameModel->GetToolStrength()); + activeTool->Strength = gameModel->GetToolStrength(); if (!held) activeTool->Draw(sim, cBrush, newPos); else @@ -432,53 +408,51 @@ void GameController::DrawPoints(int toolSelection, ui::Point oldPos, ui::Point n bool GameController::LoadClipboard() { - GameSave *clip = gameModel->GetClipboard(); + auto *clip = gameModel->GetClipboard(); if (!clip) return false; - gameModel->SetPlaceSave(clip); + gameModel->SetPlaceSave(std::make_unique(*clip)); return true; } -void GameController::LoadStamp(GameSave *stamp) +void GameController::LoadStamp(std::unique_ptr stamp) { - gameModel->SetPlaceSave(stamp); + gameModel->SetPlaceSave(std::move(stamp)); } -void GameController::TranslateSave(ui::Point point) +void GameController::TransformPlaceSave(Mat2 transform, Vec2 nudge) { - vector2d translate = v2d_new(float(point.X), float(point.Y)); - vector2d translated = gameModel->GetPlaceSave()->Translate(translate); - ui::Point currentPlaceSaveOffset = gameView->GetPlaceSaveOffset(); - // resets placeSaveOffset to 0, which is why we back it up first - gameModel->SetPlaceSave(gameModel->GetPlaceSave()); - gameView->SetPlaceSaveOffset(ui::Point(int(translated.x), int(translated.y)) + currentPlaceSaveOffset); -} - -void GameController::TransformSave(matrix2d transform) -{ - vector2d translate = v2d_zero; - gameModel->GetPlaceSave()->Transform(transform, translate); - gameModel->SetPlaceSave(gameModel->GetPlaceSave()); + gameModel->TransformPlaceSave(transform, nudge); } void GameController::ToolClick(int toolSelection, ui::Point point) { Simulation * sim = gameModel->GetSimulation(); Tool * activeTool = gameModel->GetActiveTool(toolSelection); - Brush * cBrush = gameModel->GetBrush(); - if(!activeTool || !cBrush) + Brush &cBrush = gameModel->GetBrush(); + if (!activeTool) return; activeTool->Click(sim, cBrush, point); } +static Rect SaneSaveRect(Vec2 point1, Vec2 point2) +{ + point1 = point1.Clamp(RES.OriginRect()); + point2 = point2.Clamp(RES.OriginRect()); + auto tlx = std::min(point1.X, point2.X); + auto tly = std::min(point1.Y, point2.Y); + auto brx = std::max(point1.X, point2.X); + auto bry = std::max(point1.Y, point2.Y); + return RectBetween(Vec2{ tlx, tly }, Vec2{ brx, bry }); +} + ByteString GameController::StampRegion(ui::Point point1, ui::Point point2) { - GameSave * newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), point1.X, point1.Y, point2.X, point2.Y); + auto newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), SaneSaveRect(point1, point2)); if(newSave) { newSave->paused = gameModel->GetPaused(); - ByteString stampName = Client::Ref().AddStamp(newSave); - delete newSave; + ByteString stampName = Client::Ref().AddStamp(std::move(newSave)); if (stampName.length() == 0) new ErrorMessage("Could not create stamp", "Error serializing save file"); return stampName; @@ -492,7 +466,7 @@ ByteString GameController::StampRegion(ui::Point point1, ui::Point point2) void GameController::CopyRegion(ui::Point point1, ui::Point point2) { - GameSave * newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), point1.X, point1.Y, point2.X, point2.Y); + auto newSave = gameModel->GetSimulation()->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), SaneSaveRect(point1, point2)); if(newSave) { Json::Value clipboardInfo; @@ -503,7 +477,7 @@ void GameController::CopyRegion(ui::Point point1, ui::Point point2) newSave->authors = clipboardInfo; newSave->paused = gameModel->GetPaused(); - gameModel->SetClipboard(newSave); + gameModel->SetClipboard(std::move(newSave)); } } @@ -516,20 +490,18 @@ void GameController::CutRegion(ui::Point point1, ui::Point point2) bool GameController::MouseMove(int x, int y, int dx, int dy) { - MouseMoveEvent ev(x, y, dx, dy); - return commandInterface->HandleEvent(LuaEvents::mousemove, &ev); + return commandInterface->HandleEvent(MouseMoveEvent{ x, y, dx, dy }); } bool GameController::MouseDown(int x, int y, unsigned button) { - MouseDownEvent ev(x, y, button); - bool ret = commandInterface->HandleEvent(LuaEvents::mousedown, &ev); + bool ret = commandInterface->HandleEvent(MouseDownEvent{ x, y, button }); if (ret && yGetPlacingSave() && !gameView->GetPlacingZoom()) { ui::Point point = gameModel->AdjustZoomCoords(ui::Point(x, y)); x = point.X; y = point.Y; - if (!gameModel->GetActiveTool(0) || gameModel->GetActiveTool(0)->GetIdentifier() != "DEFAULT_UI_SIGN" || button != SDL_BUTTON_LEFT) //If it's not a sign tool or you are right/middle clicking + if (!gameModel->GetActiveTool(0) || gameModel->GetActiveTool(0)->Identifier != "DEFAULT_UI_SIGN" || button != SDL_BUTTON_LEFT) //If it's not a sign tool or you are right/middle clicking { foundSignID = GetSignAt(x, y); if (foundSignID != -1) @@ -546,8 +518,7 @@ bool GameController::MouseDown(int x, int y, unsigned button) bool GameController::MouseUp(int x, int y, unsigned button, MouseupReason reason) { - MouseUpEvent ev(x, y, button, reason); - bool ret = commandInterface->HandleEvent(LuaEvents::mouseup, &ev); + bool ret = commandInterface->HandleEvent(MouseUpEvent{ x, y, button, reason }); if (reason != mouseUpNormal) return ret; if (ret && foundSignID != -1 && yGetPlacingSave()) @@ -555,7 +526,7 @@ bool GameController::MouseUp(int x, int y, unsigned button, MouseupReason reason ui::Point point = gameModel->AdjustZoomCoords(ui::Point(x, y)); x = point.X; y = point.Y; - if (!gameModel->GetActiveTool(0) || gameModel->GetActiveTool(0)->GetIdentifier() != "DEFAULT_UI_SIGN" || button != SDL_BUTTON_LEFT) //If it's not a sign tool or you are right/middle clicking + if (!gameModel->GetActiveTool(0) || gameModel->GetActiveTool(0)->Identifier != "DEFAULT_UI_SIGN" || button != SDL_BUTTON_LEFT) //If it's not a sign tool or you are right/middle clicking { int foundSignID = GetSignAt(x, y); if (foundSignID != -1) @@ -576,7 +547,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, "powdertoy.co.uk/Discussions/Thread/View.html?Thread=", str.Substr(3, si.first - 3).ToUtf8())); break; case sign::Type::Search: OpenSearch(str.Substr(3, si.first - 3)); @@ -596,26 +567,22 @@ bool GameController::MouseUp(int x, int y, unsigned button, MouseupReason reason bool GameController::MouseWheel(int x, int y, int d) { - MouseWheelEvent ev(x, y, d); - return commandInterface->HandleEvent(LuaEvents::mousewheel, &ev); + return commandInterface->HandleEvent(MouseWheelEvent{ x, y, d }); } bool GameController::TextInput(String text) { - TextInputEvent ev(text); - return commandInterface->HandleEvent(LuaEvents::textinput, &ev); + return commandInterface->HandleEvent(TextInputEvent{ text }); } bool GameController::TextEditing(String text) { - TextEditingEvent ev(text); - return commandInterface->HandleEvent(LuaEvents::textediting, &ev); + return commandInterface->HandleEvent(TextEditingEvent{ text }); } bool GameController::KeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) { - KeyEvent ev(key, scan, repeat, shift, ctrl, alt); - bool ret = commandInterface->HandleEvent(LuaEvents::keypress, &ev); + bool ret = commandInterface->HandleEvent(KeyPressEvent{ { key, scan, repeat, shift, ctrl, alt } }); if (repeat) return ret; if (ret) @@ -694,8 +661,7 @@ bool GameController::KeyPress(int key, int scan, bool repeat, bool shift, bool c bool GameController::KeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) { - KeyEvent ev(key, scan, repeat, shift, ctrl, alt); - bool ret = commandInterface->HandleEvent(LuaEvents::keyrelease, &ev); + bool ret = commandInterface->HandleEvent(KeyReleaseEvent{ { key, scan, repeat, shift, ctrl, alt } }); if (repeat) return ret; if (ret) @@ -734,17 +700,17 @@ bool GameController::KeyRelease(int key, int scan, bool repeat, bool shift, bool void GameController::Tick() { + gameModel->Tick(); if(firstTick) { -#ifdef LUACONSOLE - ((LuaScriptInterface*)commandInterface)->Init(); -#endif -#if !defined(MACOSX) && !defined(NO_INSTALL_CHECK) - if (Client::Ref().IsFirstRun()) + commandInterface->Init(); + if constexpr (INSTALL_CHECK) { - Install(); + if (Client::Ref().IsFirstRun()) + { + Install(); + } } -#endif firstTick = false; } if (gameModel->SelectNextIdentifier.length()) @@ -765,14 +731,12 @@ void GameController::Blur() { // Tell lua that mouse is up (even if it really isn't) MouseUp(0, 0, 0, mouseUpBlur); - BlurEvent ev; - commandInterface->HandleEvent(LuaEvents::blur, &ev); + commandInterface->HandleEvent(BlurEvent{}); } void GameController::Exit() { - CloseEvent ev; - commandInterface->HandleEvent(LuaEvents::close, &ev); + commandInterface->HandleEvent(CloseEvent{}); gameView->CloseActiveWindow(); HasDone = true; } @@ -892,11 +856,13 @@ void GameController::Update() gameView->SetSample(gameModel->GetSimulation()->GetSample(pos.X, pos.Y)); Simulation * sim = gameModel->GetSimulation(); - sim->BeforeSim(); if (!sim->sys_pause || sim->framerender) { - sim->UpdateParticles(0, NPART - 1); - sim->AfterSim(); + gameModel->UpdateUpTo(NPART); + } + else + { + gameModel->BeforeSim(); } //if either STKM or STK2 isn't out, reset it's selected element. Defaults to PT_DUST unless right selected is something else @@ -905,9 +871,9 @@ void GameController::Update() { int rightSelected = PT_DUST; Tool * activeTool = gameModel->GetActiveTool(1); - if (activeTool->GetIdentifier().BeginsWith("DEFAULT_PT_")) + if (activeTool->Identifier.BeginsWith("DEFAULT_PT_")) { - int sr = activeTool->GetToolID(); + int sr = activeTool->ToolID; if (sr && sim->IsElementOrNone(sr)) rightSelected = sr; } @@ -961,7 +927,8 @@ void GameController::SetToolStrength(float value) void GameController::SetZoomPosition(ui::Point position) { - ui::Point zoomPosition = position-(gameModel->GetZoomSize()/2); + auto zoomhalf = gameModel->GetZoomSize() / 2; + ui::Point zoomPosition = position - Vec2{ zoomhalf, zoomhalf }; if(zoomPosition.X < 0) zoomPosition.X = 0; if(zoomPosition.Y < 0) @@ -1102,16 +1069,16 @@ void GameController::SetActiveTool(int toolSelection, Tool * tool) gameModel->SetActiveTool(toolSelection, tool); gameModel->GetRenderer()->gravityZonesEnabled = false; if (toolSelection == 3) - gameModel->GetSimulation()->replaceModeSelected = tool->GetToolID(); + gameModel->GetSimulation()->replaceModeSelected = tool->ToolID; gameModel->SetLastTool(tool); for(int i = 0; i < 3; i++) { if(gameModel->GetActiveTool(i) == gameModel->GetMenuList().at(SC_WALL)->GetToolList().at(WL_GRAV)) gameModel->GetRenderer()->gravityZonesEnabled = true; } - if(tool->GetIdentifier() == "DEFAULT_UI_PROPERTY") + if(tool->Identifier == "DEFAULT_UI_PROPERTY") ((PropertyTool *)tool)->OpenWindow(gameModel->GetSimulation()); - if(tool->GetIdentifier() == "DEFAULT_UI_ADDLIFE") + if(tool->Identifier == "DEFAULT_UI_ADDLIFE") { ((GOLTool *)tool)->OpenWindow(gameModel->GetSimulation(), toolSelection); } @@ -1165,8 +1132,7 @@ void GameController::OpenSearch(String searchText) try { HistorySnapshot(); - gameModel->SetSave(search->GetLoadedSave(), gameView->ShiftBehaviour()); - search->ReleaseLoadedSave(); + gameModel->SetSave(search->TakeLoadedSave(), gameView->ShiftBehaviour()); } catch(GameModelException & ex) { @@ -1182,7 +1148,7 @@ void GameController::OpenSearch(String searchText) void GameController::OpenLocalSaveWindow(bool asCurrent) { Simulation * sim = gameModel->GetSimulation(); - GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour()); + auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), RES.OriginRect()); if(!gameSave) { new ErrorMessage("Error", "Unable to build save."); @@ -1191,18 +1157,18 @@ void GameController::OpenLocalSaveWindow(bool asCurrent) { gameSave->paused = gameModel->GetPaused(); - SaveFile tempSave(""); + auto tempSave = std::make_unique(""); if (gameModel->GetSaveFile()) { - tempSave.SetFileName(gameModel->GetSaveFile()->GetName()); - tempSave.SetDisplayName(gameModel->GetSaveFile()->GetDisplayName()); + tempSave->SetFileName(gameModel->GetSaveFile()->GetName()); + tempSave->SetDisplayName(gameModel->GetSaveFile()->GetDisplayName()); } - tempSave.SetGameSave(gameSave); if (!asCurrent || !gameModel->GetSaveFile()) { - new LocalSaveActivity(tempSave, [this](SaveFile *file) { - gameModel->SetSaveFile(file, gameView->ShiftBehaviour()); + tempSave->SetGameSave(std::move(gameSave)); + new LocalSaveActivity(std::move(tempSave), [this](auto file) { + gameModel->SetSaveFile(std::move(file), gameView->ShiftBehaviour()); }); } else if (gameModel->GetSaveFile()) @@ -1215,9 +1181,10 @@ void GameController::OpenLocalSaveWindow(bool asCurrent) Client::Ref().SaveAuthorInfo(&localSaveInfo); gameSave->authors = localSaveInfo; - gameModel->SetSaveFile(&tempSave, gameView->ShiftBehaviour()); Platform::MakeDirectory(LOCAL_SAVE_DIR); auto [ fromNewerVersion, saveData ] = gameSave->Serialise(); + tempSave->SetGameSave(std::move(gameSave)); + gameModel->SetSaveFile(std::move(tempSave), gameView->ShiftBehaviour()); (void)fromNewerVersion; if (saveData.size() == 0) new ErrorMessage("Error", "Unable to serialize game data."); @@ -1229,15 +1196,15 @@ void GameController::OpenLocalSaveWindow(bool asCurrent) } } -void GameController::LoadSaveFile(SaveFile * file) +void GameController::LoadSaveFile(std::unique_ptr file) { - gameModel->SetSaveFile(file, gameView->ShiftBehaviour()); + gameModel->SetSaveFile(std::move(file), gameView->ShiftBehaviour()); } -void GameController::LoadSave(SaveInfo * save) +void GameController::LoadSave(std::unique_ptr save) { - gameModel->SetSave(save, gameView->ShiftBehaviour()); + gameModel->SetSave(std::move(save), gameView->ShiftBehaviour()); } void GameController::OpenSaveDone() @@ -1247,7 +1214,7 @@ void GameController::OpenSaveDone() try { HistorySnapshot(); - LoadSave(activePreview->GetSaveInfo()); + LoadSave(activePreview->TakeSaveInfo()); } catch(GameModelException & ex) { @@ -1258,7 +1225,7 @@ void GameController::OpenSaveDone() void GameController::OpenSavePreview(int saveID, int saveDate, bool instant) { - activePreview = new PreviewController(saveID, saveDate, instant, [this] { OpenSaveDone(); }); + activePreview = new PreviewController(saveID, saveDate, instant, [this] { OpenSaveDone(); }, nullptr); ui::Engine::Ref().ShowWindow(activePreview->GetView()); } @@ -1266,16 +1233,16 @@ void GameController::OpenSavePreview() { if(gameModel->GetSave()) { - activePreview = new PreviewController(gameModel->GetSave()->GetID(), 0, false, [this] { OpenSaveDone(); }); + activePreview = new PreviewController(gameModel->GetSave()->GetID(), 0, false, [this] { OpenSaveDone(); }, nullptr); ui::Engine::Ref().ShowWindow(activePreview->GetView()); } } void GameController::OpenLocalBrowse() { - new FileBrowserActivity(LOCAL_SAVE_DIR PATH_SEP, [this](std::unique_ptr file) { + new FileBrowserActivity(ByteString::Build(LOCAL_SAVE_DIR, PATH_SEP_CHAR), [this](auto file) { HistorySnapshot(); - LoadSaveFile(file.get()); + LoadSaveFile(std::move(file)); }); } @@ -1345,14 +1312,14 @@ void GameController::OpenTags() void GameController::OpenStamps() { localBrowser = new LocalBrowserController([this] { - SaveFile *file = localBrowser->GetSave(); + auto file = localBrowser->TakeSave(); if (file) { if (file->GetError().length()) new ErrorMessage("Error loading stamp", file->GetError()); else if (localBrowser->GetMoveToFront()) Client::Ref().MoveStampToFront(file->GetDisplayName().ToUtf8()); - LoadStamp(file->GetGameSave()); + LoadStamp(file->TakeGameSave()); } }); ui::Engine::Ref().ShowWindow(localBrowser->GetView()); @@ -1362,7 +1329,6 @@ void GameController::OpenOptions() { options = new OptionsController(gameModel, [this] { gameModel->UpdateQuickOptions(); - Client::Ref().WritePrefs(); // * I don't think there's a reason for this but I'm too lazy to check. -- LBPHacker }); ui::Engine::Ref().ShowWindow(options->GetView()); @@ -1394,7 +1360,7 @@ void GameController::OpenSaveWindow() if(gameModel->GetUser().UserID) { Simulation * sim = gameModel->GetSimulation(); - GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour()); + auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), RES.OriginRect()); if(!gameSave) { new ErrorMessage("Error", "Unable to build save."); @@ -1405,22 +1371,22 @@ void GameController::OpenSaveWindow() if(gameModel->GetSave()) { - SaveInfo tempSave(*gameModel->GetSave()); - tempSave.SetGameSave(gameSave); - new ServerSaveActivity(tempSave, [this](SaveInfo &save) { - save.SetVote(1); - save.SetVotesUp(1); - LoadSave(&save); + auto tempSave = gameModel->GetSave()->CloneInfo(); + tempSave->SetGameSave(std::move(gameSave)); + new ServerSaveActivity(std::move(tempSave), [this](auto save) { + save->SetVote(1); + save->SetVotesUp(1); + LoadSave(std::move(save)); }); } else { - SaveInfo tempSave(0, 0, 0, 0, 0, gameModel->GetUser().Username, ""); - tempSave.SetGameSave(gameSave); - new ServerSaveActivity(tempSave, [this](SaveInfo &save) { - save.SetVote(1); - save.SetVotesUp(1); - LoadSave(&save); + auto tempSave = std::make_unique(0, 0, 0, 0, 0, gameModel->GetUser().Username, ""); + tempSave->SetGameSave(std::move(gameSave)); + new ServerSaveActivity(std::move(tempSave), [this](auto save) { + save->SetVote(1); + save->SetVotesUp(1); + LoadSave(std::move(save)); }); } } @@ -1436,7 +1402,7 @@ void GameController::SaveAsCurrent() if(gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetUser().Username == gameModel->GetSave()->GetUserName()) { Simulation * sim = gameModel->GetSimulation(); - GameSave * gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour()); + auto gameSave = sim->Save(gameModel->GetIncludePressure() != gameView->ShiftBehaviour(), RES.OriginRect()); if(!gameSave) { new ErrorMessage("Error", "Unable to build save."); @@ -1447,15 +1413,15 @@ void GameController::SaveAsCurrent() if(gameModel->GetSave()) { - SaveInfo tempSave(*gameModel->GetSave()); - tempSave.SetGameSave(gameSave); - new ServerSaveActivity(tempSave, true, [this](SaveInfo &save) { LoadSave(&save); }); + auto tempSave = gameModel->GetSave()->CloneInfo(); + tempSave->SetGameSave(std::move(gameSave)); + new ServerSaveActivity(std::move(tempSave), true, [this](auto save) { LoadSave(std::move(save)); }); } else { - SaveInfo tempSave(0, 0, 0, 0, 0, gameModel->GetUser().Username, ""); - tempSave.SetGameSave(gameSave); - new ServerSaveActivity(tempSave, true, [this](SaveInfo &save) { LoadSave(&save); }); + auto tempSave = std::make_unique(0, 0, 0, 0, 0, gameModel->GetUser().Username, ""); + tempSave->SetGameSave(std::move(gameSave)); + new ServerSaveActivity(std::move(tempSave), true, [this](auto save) { LoadSave(std::move(save)); }); } } } @@ -1477,24 +1443,17 @@ void GameController::FrameStep() void GameController::Vote(int direction) { - if(gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetSave()->GetID()) + if (gameModel->GetSave() && gameModel->GetUser().UserID && gameModel->GetSave()->GetID()) { - try - { - gameModel->SetVote(direction); - } - catch(GameModelException & ex) - { - new ErrorMessage("Error while voting", ByteString(ex.what()).FromUtf8()); - } + gameModel->SetVote(direction); } } void GameController::ChangeBrush() { - auto prev_size = gameModel->GetBrush()->GetRadius(); + auto prev_size = gameModel->GetBrush().GetRadius(); gameModel->SetBrushID(gameModel->GetBrushID()+1); - gameModel->GetBrush()->SetRadius(prev_size); + gameModel->GetBrush().SetRadius(prev_size); } void GameController::ClearSim() @@ -1530,12 +1489,12 @@ void GameController::ReloadSim() if(gameModel->GetSave() && gameModel->GetSave()->GetGameSave()) { HistorySnapshot(); - gameModel->SetSave(gameModel->GetSave(), gameView->ShiftBehaviour()); + gameModel->SetSave(gameModel->TakeSave(), gameView->ShiftBehaviour()); } else if(gameModel->GetSaveFile() && gameModel->GetSaveFile()->GetGameSave()) { HistorySnapshot(); - gameModel->SetSaveFile(gameModel->GetSaveFile(), gameView->ShiftBehaviour()); + gameModel->SetSaveFile(gameModel->TakeSaveFile(), gameView->ShiftBehaviour()); } } @@ -1573,7 +1532,7 @@ void GameController::NotifyAuthUserChanged(Client * sender) gameModel->SetUser(newUser); } -void GameController::NotifyNewNotification(Client * sender, std::pair notification) +void GameController::NotifyNewNotification(Client * sender, ServerNotification notification) { class LinkNotification : public Notification { @@ -1587,7 +1546,7 @@ void GameController::NotifyNewNotification(Client * sender, std::pairAddNotification(new LinkNotification(notification.second, notification.first)); + gameModel->AddNotification(new LinkNotification(notification.link, notification.text)); } void GameController::NotifyUpdateAvailable(Client * sender) @@ -1601,56 +1560,89 @@ void GameController::NotifyUpdateAvailable(Client * sender) void Action() override { - UpdateInfo info = Client::Ref().GetUpdateInfo(); + auto optinfo = Client::Ref().GetUpdateInfo(); + if (!optinfo.has_value()) + { + std::cerr << "odd, the update has disappeared" << std::endl; + return; + } + UpdateInfo info = optinfo.value(); StringBuilder updateMessage; -#ifndef MACOSX - updateMessage << "Are you sure you want to run the updater? Please save any changes before updating.\n\nCurrent version:\n "; -#else - updateMessage << "Click \"Continue\" to download the latest version from our website.\n\nCurrent version:\n "; -#endif + if (Platform::CanUpdate()) + { + updateMessage << "Are you sure you want to run the updater? Please save any changes before updating.\n\nCurrent version:\n "; + } + else + { + updateMessage << "Click \"Continue\" to download the latest version from our website.\n\nCurrent version:\n "; + } -#ifdef SNAPSHOT - updateMessage << "Snapshot " << SNAPSHOT_ID; -#elif MOD_ID > 0 - updateMessage << "Mod version " << SNAPSHOT_ID; -#elif defined(BETA) - updateMessage << SAVE_VERSION << "." << MINOR_VERSION << " Beta, Build " << BUILD_NUM; -#else - updateMessage << SAVE_VERSION << "." << MINOR_VERSION << " Stable, Build " << BUILD_NUM; -#endif + if constexpr (SNAPSHOT) + { + updateMessage << "Snapshot " << SNAPSHOT_ID; + } + else if constexpr (MOD) + { + updateMessage << "Mod version " << SNAPSHOT_ID; + } + else if constexpr (BETA) + { + updateMessage << SAVE_VERSION << "." << MINOR_VERSION << " Beta, Build " << BUILD_NUM; + } + else + { + updateMessage << SAVE_VERSION << "." << MINOR_VERSION << " Stable, Build " << BUILD_NUM; + } updateMessage << "\nNew version:\n "; - if (info.Type == UpdateInfo::Beta) - updateMessage << info.Major << "." << info.Minor << " Beta, Build " << info.Build; - else if (info.Type == UpdateInfo::Snapshot) -#if MOD_ID > 0 - updateMessage << "Mod version " << info.Time; -#else - updateMessage << "Snapshot " << info.Time; -#endif - else if(info.Type == UpdateInfo::Stable) - updateMessage << info.Major << "." << info.Minor << " Stable, Build " << info.Build; + if (info.channel == UpdateInfo::channelBeta) + { + updateMessage << info.major << "." << info.minor << " Beta, Build " << info.build; + } + else if (info.channel == UpdateInfo::channelSnapshot) + { + if constexpr (MOD) + { + updateMessage << "Mod version " << info.build; + } + else + { + updateMessage << "Snapshot " << info.build; + } + } + else if(info.channel == UpdateInfo::channelStable) + { + updateMessage << info.major << "." << info.minor << " Stable, Build " << info.build; + } - if (info.Changelog.length()) - updateMessage << "\n\nChangelog:\n" << info.Changelog; + if (info.changeLog.length()) + updateMessage << "\n\nChangelog:\n" << info.changeLog; - new ConfirmPrompt("Run Updater", updateMessage.Build(), { [this] { c->RunUpdater(); } }); + new ConfirmPrompt("Run Updater", updateMessage.Build(), { [this, info] { c->RunUpdater(info); } }); } }; - switch(sender->GetUpdateInfo().Type) + auto optinfo = sender->GetUpdateInfo(); + if (!optinfo.has_value()) { - case UpdateInfo::Snapshot: -#if MOD_ID > 0 - gameModel->AddNotification(new UpdateNotification(this, "A new mod update is available - click here to update")); -#else - gameModel->AddNotification(new UpdateNotification(this, "A new snapshot is available - click here to update")); -#endif + return; + } + switch(optinfo.value().channel) + { + case UpdateInfo::channelSnapshot: + if constexpr (MOD) + { + gameModel->AddNotification(new UpdateNotification(this, "A new mod update is available - click here to update")); + } + else + { + gameModel->AddNotification(new UpdateNotification(this, "A new snapshot is available - click here to update")); + } break; - case UpdateInfo::Stable: + case UpdateInfo::channelStable: gameModel->AddNotification(new UpdateNotification(this, "A new version is available - click here to update")); break; - case UpdateInfo::Beta: + case UpdateInfo::channelBeta: gameModel->AddNotification(new UpdateNotification(this, "A new beta is available - click here to update")); break; } @@ -1661,21 +1653,17 @@ void GameController::RemoveNotification(Notification * notification) gameModel->RemoveNotification(notification); } -void GameController::RunUpdater() +void GameController::RunUpdater(UpdateInfo info) { -#ifndef MACOSX - Exit(); - new UpdateActivity(); -#else - -#ifdef UPDATESERVER - ByteString file = ByteString::Build(SCHEME, UPDATESERVER, Client::Ref().GetUpdateInfo().File); -#else - ByteString file = ByteString::Build(SCHEME, SERVER, Client::Ref().GetUpdateInfo().File); -#endif - - Platform::OpenURI(file); -#endif // MACOSX + if (Platform::CanUpdate()) + { + Exit(); + new UpdateActivity(info); + } + else + { + Platform::OpenURI(info.file); + } } bool GameController::GetMouseClickRequired() diff --git a/src/gui/game/GameController.h b/src/gui/game/GameController.h index a3364f32e..d09b8e3f1 100644 --- a/src/gui/game/GameController.h +++ b/src/gui/game/GameController.h @@ -1,21 +1,15 @@ -#ifndef GAMECONTROLLER_H -#define GAMECONTROLLER_H -#include "Config.h" - +#pragma once +#include "client/ClientListener.h" +#include "client/StartupInfo.h" +#include "gui/interface/Point.h" +#include "gui/interface/Colour.h" +#include "simulation/Sign.h" +#include "simulation/Particle.h" +#include "Misc.h" #include #include #include -#include "client/ClientListener.h" - -#include "gui/interface/Point.h" -#include "gui/interface/Colour.h" - -#include "simulation/Sign.h" -#include "simulation/Particle.h" - -#include "Misc.h" - class DebugInfo; class SaveFile; class Notification; @@ -28,6 +22,7 @@ class SearchController; class PreviewController; class RenderController; class CommandInterface; +class VideoBuffer; class Tool; class Menu; class SaveInfo; @@ -51,7 +46,6 @@ private: TagsController * tagsWindow; LocalBrowserController * localBrowser; OptionsController * options; - CommandInterface * commandInterface; std::vector debugInfo; std::unique_ptr beforeRestore; unsigned int debugFlags; @@ -135,8 +129,8 @@ public: void SetActiveColourPreset(int preset); void SetColour(ui::Colour colour); void SetToolStrength(float value); - void LoadSaveFile(SaveFile * file); - void LoadSave(SaveInfo * save); + void LoadSaveFile(std::unique_ptr file); + void LoadSave(std::unique_ptr save); void OpenSearch(String searchText); void OpenLogin(); void OpenProfile(); @@ -160,10 +154,10 @@ public: void ShowConsole(); void HideConsole(); void FrameStep(); - void TranslateSave(ui::Point point); - void TransformSave(matrix2d transform); + void TransformPlaceSave(Mat2 transform, Vec2 nudge); bool MouseInZoom(ui::Point position); ui::Point PointTranslate(ui::Point point); + ui::Point PointTranslateNoClamp(ui::Point point); ui::Point NormaliseBlockCoord(ui::Point point); String ElementResolve(int type, int ctype); String BasicParticleInfo(Particle const &sample_part); @@ -182,17 +176,15 @@ public: void ToggleNewtonianGravity(); bool LoadClipboard(); - void LoadStamp(GameSave *stamp); + void LoadStamp(std::unique_ptr stamp); void RemoveNotification(Notification * notification); void NotifyUpdateAvailable(Client * sender) override; void NotifyAuthUserChanged(Client * sender) override; - void NotifyNewNotification(Client * sender, std::pair notification) override; - void RunUpdater(); + void NotifyNewNotification(Client * sender, ServerNotification notification) override; + void RunUpdater(UpdateInfo info); bool GetMouseClickRequired(); void RemoveCustomGOLType(const ByteString &identifier); }; - -#endif // GAMECONTROLLER_H diff --git a/src/gui/game/GameControllerEvents.h b/src/gui/game/GameControllerEvents.h new file mode 100644 index 000000000..f93f24ca5 --- /dev/null +++ b/src/gui/game/GameControllerEvents.h @@ -0,0 +1,97 @@ +#pragma once +#include "common/String.h" +#include + +struct TextInputEvent +{ + String text; +}; + +struct TextEditingEvent +{ + String text; +}; + +struct KeyEvent +{ + int key; + int scan; + bool repeat; + bool shift; + bool ctrl; + bool alt; +}; + +struct KeyPressEvent : public KeyEvent +{ +}; + +struct KeyReleaseEvent : public KeyEvent +{ +}; + +struct MouseDownEvent +{ + int x; + int y; + unsigned int button; +}; + +struct MouseUpEvent +{ + int x; + int y; + unsigned int button; + int reason; +}; + +struct MouseMoveEvent +{ + int x; + int y; + int dx; + int dy; +}; + +struct MouseWheelEvent +{ + int x; + int y; + int d; +}; + +struct TickEvent +{ +}; + +struct BlurEvent +{ +}; + +struct CloseEvent +{ +}; + +struct BeforeSimEvent +{ +}; + +struct AfterSimEvent +{ +}; + +using GameControllerEvent = std::variant< + TextInputEvent, + TextEditingEvent, + KeyPressEvent, + KeyReleaseEvent, + MouseDownEvent, + MouseUpEvent, + MouseMoveEvent, + MouseWheelEvent, + TickEvent, + BlurEvent, + CloseEvent, + BeforeSimEvent, + AfterSimEvent +>; diff --git a/src/gui/game/GameModel.cpp b/src/gui/game/GameModel.cpp index b5d112c91..6a6b86bfd 100644 --- a/src/gui/game/GameModel.cpp +++ b/src/gui/game/GameModel.cpp @@ -1,8 +1,4 @@ #include "GameModel.h" - -#include -#include - #include "BitmapBrush.h" #include "EllipseBrush.h" #include "Favorite.h" @@ -12,27 +8,33 @@ #include "GameView.h" #include "Menu.h" #include "Notification.h" +#include "RectangleBrush.h" #include "TriangleBrush.h" #include "QuickOptions.h" - +#include "lua/CommandInterface.h" +#include "prefs/GlobalPrefs.h" #include "client/Client.h" #include "client/GameSave.h" #include "client/SaveFile.h" #include "client/SaveInfo.h" -#include "common/Platform.h" +#include "client/http/ExecVoteRequest.h" +#include "common/platform/Platform.h" #include "graphics/Renderer.h" #include "simulation/Air.h" #include "simulation/GOLString.h" -#include "simulation/Gravity.h" +#include "simulation/gravity/Gravity.h" #include "simulation/Simulation.h" #include "simulation/Snapshot.h" #include "simulation/SnapshotDelta.h" #include "simulation/ElementClasses.h" #include "simulation/ElementGraphics.h" #include "simulation/ToolClasses.h" - #include "gui/game/DecorationTool.h" #include "gui/interface/Engine.h" +#include "gui/dialogues/ErrorMessage.h" +#include +#include +#include HistoryEntry::~HistoryEntry() { @@ -41,12 +43,8 @@ HistoryEntry::~HistoryEntry() } GameModel::GameModel(): - clipboard(NULL), - placeSave(NULL), activeMenu(-1), currentBrush(0), - currentSave(NULL), - currentFile(NULL), currentUser(0, ""), toolStrength(1.0f), historyPosition(0), @@ -58,7 +56,7 @@ GameModel::GameModel(): decoSpace(0) { sim = new Simulation(); - ren = new Renderer(ui::Engine::Ref().g, sim); + ren = new Renderer(sim); activeTools = regularToolset; @@ -66,56 +64,51 @@ GameModel::GameModel(): std::fill(regularToolset, regularToolset+4, (Tool*)NULL); //Default render prefs - std::vector tempArray; - tempArray.push_back(RENDER_FIRE); - tempArray.push_back(RENDER_EFFE); - tempArray.push_back(RENDER_BASC); - ren->SetRenderMode(tempArray); - tempArray.clear(); - - ren->SetDisplayMode(tempArray); - + ren->SetRenderMode({ + RENDER_FIRE, + RENDER_EFFE, + RENDER_BASC, + }); + ren->SetDisplayMode({}); ren->SetColourMode(0); //Load config into renderer - ren->SetColourMode(Client::Ref().GetPrefUInteger("Renderer.ColourMode", 0)); + auto &prefs = GlobalPrefs::Ref(); + ren->SetColourMode(prefs.Get("Renderer.ColourMode", 0U)); - tempArray = Client::Ref().GetPrefUIntegerArray("Renderer.DisplayModes"); - if(tempArray.size()) + auto displayModes = prefs.Get("Renderer.DisplayModes", std::vector{}); + if (displayModes.size()) { - std::vector displayModes(tempArray.begin(), tempArray.end()); ren->SetDisplayMode(displayModes); } - - tempArray = Client::Ref().GetPrefUIntegerArray("Renderer.RenderModes"); - if(tempArray.size()) + auto renderModes = prefs.Get("Renderer.RenderModes", std::vector{}); + if (renderModes.size()) { - std::vector renderModes(tempArray.begin(), tempArray.end()); ren->SetRenderMode(renderModes); } - ren->gravityFieldEnabled = Client::Ref().GetPrefBool("Renderer.GravityField", false); - ren->decorations_enable = Client::Ref().GetPrefBool("Renderer.Decorations", true); + ren->gravityFieldEnabled = prefs.Get("Renderer.GravityField", false); + ren->decorations_enable = prefs.Get("Renderer.Decorations", true); //Load config into simulation - edgeMode = Client::Ref().GetPrefInteger("Simulation.EdgeMode", 0); + edgeMode = prefs.Get("Simulation.EdgeMode", 0); // TODO: EdgeMode enum sim->SetEdgeMode(edgeMode); ambientAirTemp = float(R_TEMP) + 273.15f; { - auto temp = Client::Ref().GetPrefNumber("Simulation.AmbientAirTemp", ambientAirTemp); + auto temp = prefs.Get("Simulation.AmbientAirTemp", ambientAirTemp); if (MIN_TEMP <= temp && MAX_TEMP >= temp) { ambientAirTemp = temp; } } sim->air->ambientAirTemp = ambientAirTemp; - decoSpace = Client::Ref().GetPrefInteger("Simulation.DecoSpace", 0); + decoSpace = prefs.Get("Simulation.DecoSpace", 0); // TODO: DecoSpace enum sim->SetDecoSpace(decoSpace); - int ngrav_enable = Client::Ref().GetPrefInteger("Simulation.NewtonianGravity", 0); + int ngrav_enable = prefs.Get("Simulation.NewtonianGravity", 0); // TODO: NewtonianGravity enum if (ngrav_enable) sim->grav->start_grav_async(); - sim->aheat_enable = Client::Ref().GetPrefInteger("Simulation.AmbientHeat", 0); - sim->pretty_powder = Client::Ref().GetPrefInteger("Simulation.PrettyPowder", 0); + sim->aheat_enable = prefs.Get("Simulation.AmbientHeat", 0); // TODO: AmbientHeat enum + sim->pretty_powder = prefs.Get("Simulation.PrettyPowder", 0); // TODO: PrettyPowder enum Favorite::Ref().LoadFavoritesFromPrefs(); @@ -127,14 +120,14 @@ GameModel::GameModel(): BuildMenus(); - perfectCircle = Client::Ref().GetPrefBool("PerfectCircleBrush", true); + perfectCircle = prefs.Get("PerfectCircleBrush", true); BuildBrushList(); //Set default decoration colour - unsigned char colourR = std::min(Client::Ref().GetPrefInteger("Decoration.Red", 200), 255); - unsigned char colourG = std::min(Client::Ref().GetPrefInteger("Decoration.Green", 100), 255); - unsigned char colourB = std::min(Client::Ref().GetPrefInteger("Decoration.Blue", 50), 255); - unsigned char colourA = std::min(Client::Ref().GetPrefInteger("Decoration.Alpha", 255), 255); + unsigned char colourR = std::max(std::min(prefs.Get("Decoration.Red", 200), 255), 0); + unsigned char colourG = std::max(std::min(prefs.Get("Decoration.Green", 100), 255), 0); + unsigned char colourB = std::max(std::min(prefs.Get("Decoration.Blue", 50), 255), 0); + unsigned char colourA = std::max(std::min(prefs.Get("Decoration.Alpha", 255), 255), 0); SetColourSelectorColour(ui::Colour(colourR, colourG, colourB, colourA)); @@ -147,41 +140,38 @@ GameModel::GameModel(): colourPresets.push_back(ui::Colour(0, 0, 255)); colourPresets.push_back(ui::Colour(0, 0, 0)); - undoHistoryLimit = Client::Ref().GetPrefInteger("Simulation.UndoHistoryLimit", 5); + undoHistoryLimit = prefs.Get("Simulation.UndoHistoryLimit", 5U); // cap due to memory usage (this is about 3.4GB of RAM) if (undoHistoryLimit > 200) SetUndoHistoryLimit(200); - mouseClickRequired = Client::Ref().GetPrefBool("MouseClickRequired", false); - includePressure = Client::Ref().GetPrefBool("Simulation.IncludePressure", true); - temperatureScale = Client::Ref().GetPrefInteger("Renderer.TemperatureScale", 1); + mouseClickRequired = prefs.Get("MouseClickRequired", false); + includePressure = prefs.Get("Simulation.IncludePressure", true); + temperatureScale = prefs.Get("Renderer.TemperatureScale", 1); // TODO: TemperatureScale enum ClearSimulation(); } GameModel::~GameModel() { - //Save to config: - Client::Ref().SetPref("Renderer.ColourMode", ren->GetColourMode()); - - std::vector displayModes = ren->GetDisplayMode(); - Client::Ref().SetPref("Renderer.DisplayModes", std::vector(displayModes.begin(), displayModes.end())); - - std::vector renderModes = ren->GetRenderMode(); - Client::Ref().SetPref("Renderer.RenderModes", std::vector(renderModes.begin(), renderModes.end())); - - Client::Ref().SetPref("Renderer.GravityField", (bool)ren->gravityFieldEnabled); - Client::Ref().SetPref("Renderer.Decorations", (bool)ren->decorations_enable); - Client::Ref().SetPref("Renderer.DebugMode", ren->debugLines); //These two should always be equivalent, even though they are different things - - Client::Ref().SetPref("Simulation.NewtonianGravity", sim->grav->IsEnabled()); - Client::Ref().SetPref("Simulation.AmbientHeat", sim->aheat_enable); - Client::Ref().SetPref("Simulation.PrettyPowder", sim->pretty_powder); - - Client::Ref().SetPref("Decoration.Red", (int)colour.Red); - Client::Ref().SetPref("Decoration.Green", (int)colour.Green); - Client::Ref().SetPref("Decoration.Blue", (int)colour.Blue); - Client::Ref().SetPref("Decoration.Alpha", (int)colour.Alpha); + auto &prefs = GlobalPrefs::Ref(); + { + //Save to config: + Prefs::DeferWrite dw(prefs); + prefs.Set("Renderer.ColourMode", ren->GetColourMode()); + prefs.Set("Renderer.DisplayModes", ren->GetDisplayMode()); + prefs.Set("Renderer.RenderModes", ren->GetRenderMode()); + prefs.Set("Renderer.GravityField", (bool)ren->gravityFieldEnabled); + prefs.Set("Renderer.Decorations", (bool)ren->decorations_enable); + prefs.Set("Renderer.DebugMode", ren->debugLines); //These two should always be equivalent, even though they are different things + prefs.Set("Simulation.NewtonianGravity", sim->grav->IsEnabled()); + prefs.Set("Simulation.AmbientHeat", sim->aheat_enable); + prefs.Set("Simulation.PrettyPowder", sim->pretty_powder); + prefs.Set("Decoration.Red", (int)colour.Red); + prefs.Set("Decoration.Green", (int)colour.Green); + prefs.Set("Decoration.Blue", (int)colour.Blue); + prefs.Set("Decoration.Alpha", (int)colour.Alpha); + } for (size_t i = 0; i < menuList.size(); i++) { @@ -193,16 +183,8 @@ GameModel::~GameModel() { delete *iter; } - for (size_t i = 0; i < brushList.size(); i++) - { - delete brushList[i]; - } delete sim; delete ren; - delete placeSave; - delete clipboard; - delete currentSave; - delete currentFile; //if(activeTools) // delete[] activeTools; } @@ -243,13 +225,13 @@ void GameModel::BuildMenus() ByteString activeToolIdentifiers[4]; if(regularToolset[0]) - activeToolIdentifiers[0] = regularToolset[0]->GetIdentifier(); + activeToolIdentifiers[0] = regularToolset[0]->Identifier; if(regularToolset[1]) - activeToolIdentifiers[1] = regularToolset[1]->GetIdentifier(); + activeToolIdentifiers[1] = regularToolset[1]->Identifier; if(regularToolset[2]) - activeToolIdentifiers[2] = regularToolset[2]->GetIdentifier(); + activeToolIdentifiers[2] = regularToolset[2]->Identifier; if(regularToolset[3]) - activeToolIdentifiers[3] = regularToolset[3]->GetIdentifier(); + activeToolIdentifiers[3] = regularToolset[3]->Identifier; //Empty current menus for (size_t i = 0; i < menuList.size(); i++) @@ -282,19 +264,19 @@ void GameModel::BuildMenus() Tool * tempTool; if(i == PT_LIGH) { - tempTool = new Element_LIGH_Tool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator); + tempTool = new Element_LIGH_Tool(i, sim->elements[i].Name, sim->elements[i].Description, sim->elements[i].Colour, sim->elements[i].Identifier, sim->elements[i].IconGenerator); } else if(i == PT_TESC) { - tempTool = new Element_TESC_Tool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator); + tempTool = new Element_TESC_Tool(i, sim->elements[i].Name, sim->elements[i].Description, sim->elements[i].Colour, sim->elements[i].Identifier, sim->elements[i].IconGenerator); } else if(i == PT_STKM || i == PT_FIGH || i == PT_STKM2) { - tempTool = new PlopTool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator); + tempTool = new PlopTool(i, sim->elements[i].Name, sim->elements[i].Description, sim->elements[i].Colour, sim->elements[i].Identifier, sim->elements[i].IconGenerator); } else { - tempTool = new ElementTool(i, sim->elements[i].Name, sim->elements[i].Description, PIXR(sim->elements[i].Colour), PIXG(sim->elements[i].Colour), PIXB(sim->elements[i].Colour), sim->elements[i].Identifier, sim->elements[i].IconGenerator); + tempTool = new ElementTool(i, sim->elements[i].Name, sim->elements[i].Description, sim->elements[i].Colour, sim->elements[i].Identifier, sim->elements[i].IconGenerator); } if (sim->elements[i].MenuSection >= 0 && sim->elements[i].MenuSection < SC_TOTAL && sim->elements[i].MenuVisible) @@ -312,18 +294,21 @@ void GameModel::BuildMenus() //Build menu for GOL types for(int i = 0; i < NGOL; i++) { - Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(i), builtinGol[i].name, builtinGol[i].description, PIXR(builtinGol[i].colour), PIXG(builtinGol[i].colour), PIXB(builtinGol[i].colour), "DEFAULT_PT_LIFE_"+builtinGol[i].name.ToAscii()); + Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(i), builtinGol[i].name, builtinGol[i].description, builtinGol[i].colour, "DEFAULT_PT_LIFE_"+builtinGol[i].name.ToAscii()); menuList[SC_LIFE]->AddTool(tempTool); } { - auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types"); - Json::Value validatedCustomLifeTypes(Json::arrayValue); + auto &prefs = GlobalPrefs::Ref(); + auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector{}); + std::vector validatedCustomLifeTypes; std::vector newCustomGol; + bool removedAny = false; for (auto gol : customGOLTypes) { auto parts = gol.FromUtf8().PartitionBy(' '); if (parts.size() != 4) { + removedAny = true; continue; } Simulation::CustomGOLData gd; @@ -333,11 +318,13 @@ void GameModel::BuildMenus() auto &colour2String = parts[3]; if (!ValidateGOLName(gd.nameString)) { + removedAny = true; continue; } gd.rule = ParseGOLString(gd.ruleString); if (gd.rule == -1) { + removedAny = true; continue; } try @@ -347,16 +334,20 @@ void GameModel::BuildMenus() } catch (std::exception &) { + removedAny = true; continue; } newCustomGol.push_back(gd); - validatedCustomLifeTypes.append(gol); + validatedCustomLifeTypes.push_back(gol); + } + if (removedAny) + { + // All custom rules that fail validation will be removed + prefs.Set("CustomGOL.Types", validatedCustomLifeTypes); } - // All custom rules that fail validation will be removed - Client::Ref().SetPref("CustomGOL.Types", validatedCustomLifeTypes); for (auto &gd : newCustomGol) { - Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(gd.rule), gd.nameString, "Custom GOL type: " + gd.ruleString, PIXR(gd.colour1), PIXG(gd.colour1), PIXB(gd.colour1), "DEFAULT_PT_LIFECUST_"+gd.nameString.ToAscii(), NULL); + Tool * tempTool = new ElementTool(PT_LIFE|PMAPID(gd.rule), gd.nameString, "Custom GOL type: " + gd.ruleString, RGB::Unpack(gd.colour1), "DEFAULT_PT_LIFECUST_"+gd.nameString.ToAscii(), NULL); menuList[SC_LIFE]->AddTool(tempTool); } sim->SetCustomGOL(newCustomGol); @@ -365,7 +356,7 @@ void GameModel::BuildMenus() //Build other menus from wall data for(int i = 0; i < UI_WALLCOUNT; i++) { - Tool * tempTool = new WallTool(i, "", sim->wtypes[i].descs, PIXR(sim->wtypes[i].colour), PIXG(sim->wtypes[i].colour), PIXB(sim->wtypes[i].colour), sim->wtypes[i].identifier, sim->wtypes[i].textureGen); + Tool * tempTool = new WallTool(i, sim->wtypes[i].descs, sim->wtypes[i].colour, sim->wtypes[i].identifier, sim->wtypes[i].textureGen); menuList[SC_WALL]->AddTool(tempTool); //sim->wtypes[i] } @@ -377,28 +368,26 @@ void GameModel::BuildMenus() i, sim->tools[i].Name, sim->tools[i].Description, - PIXR(sim->tools[i].Colour), - PIXG(sim->tools[i].Colour), - PIXB(sim->tools[i].Colour), + sim->tools[i].Colour, sim->tools[i].Identifier ); menuList[SC_TOOL]->AddTool(tempTool); } //Add special sign and prop tools - menuList[SC_TOOL]->AddTool(new WindTool(0, "WIND", "Creates air movement.", 64, 64, 64, "DEFAULT_UI_WIND")); - menuList[SC_TOOL]->AddTool(new PropertyTool(this)); - menuList[SC_TOOL]->AddTool(new SignTool(this)); - menuList[SC_TOOL]->AddTool(new SampleTool(this)); - menuList[SC_LIFE]->AddTool(new GOLTool(this)); + menuList[SC_TOOL]->AddTool(new WindTool()); + menuList[SC_TOOL]->AddTool(new PropertyTool(*this)); + menuList[SC_TOOL]->AddTool(new SignTool(*this)); + menuList[SC_TOOL]->AddTool(new SampleTool(*this)); + menuList[SC_LIFE]->AddTool(new GOLTool(*this)); //Add decoration tools to menu - menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_ADD, "ADD", "Colour blending: Add.", 0, 0, 0, "DEFAULT_DECOR_ADD")); - menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_SUBTRACT, "SUB", "Colour blending: Subtract.", 0, 0, 0, "DEFAULT_DECOR_SUB")); - menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_MULTIPLY, "MUL", "Colour blending: Multiply.", 0, 0, 0, "DEFAULT_DECOR_MUL")); - menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_DIVIDE, "DIV", "Colour blending: Divide." , 0, 0, 0, "DEFAULT_DECOR_DIV")); - menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_SMUDGE, "SMDG", "Smudge tool, blends surrounding deco together.", 0, 0, 0, "DEFAULT_DECOR_SMDG")); - menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_CLEAR, "CLR", "Erase any set decoration.", 0, 0, 0, "DEFAULT_DECOR_CLR")); - menuList[SC_DECO]->AddTool(new DecorationTool(ren, DECO_DRAW, "SET", "Draw decoration (No blending).", 0, 0, 0, "DEFAULT_DECOR_SET")); + menuList[SC_DECO]->AddTool(new DecorationTool(*ren, DECO_ADD, "ADD", "Colour blending: Add.", 0x000000_rgb, "DEFAULT_DECOR_ADD")); + menuList[SC_DECO]->AddTool(new DecorationTool(*ren, DECO_SUBTRACT, "SUB", "Colour blending: Subtract.", 0x000000_rgb, "DEFAULT_DECOR_SUB")); + menuList[SC_DECO]->AddTool(new DecorationTool(*ren, DECO_MULTIPLY, "MUL", "Colour blending: Multiply.", 0x000000_rgb, "DEFAULT_DECOR_MUL")); + menuList[SC_DECO]->AddTool(new DecorationTool(*ren, DECO_DIVIDE, "DIV", "Colour blending: Divide." , 0x000000_rgb, "DEFAULT_DECOR_DIV")); + menuList[SC_DECO]->AddTool(new DecorationTool(*ren, DECO_SMUDGE, "SMDG", "Smudge tool, blends surrounding deco together.", 0x000000_rgb, "DEFAULT_DECOR_SMDG")); + menuList[SC_DECO]->AddTool(new DecorationTool(*ren, DECO_CLEAR, "CLR", "Erase any set decoration.", 0x000000_rgb, "DEFAULT_DECOR_CLR")); + menuList[SC_DECO]->AddTool(new DecorationTool(*ren, DECO_DRAW, "SET", "Draw decoration (No blending).", 0x000000_rgb, "DEFAULT_DECOR_SET")); SetColourSelectorColour(colour); // update tool colors decoToolset[0] = GetToolFromIdentifier("DEFAULT_DECOR_SET"); decoToolset[1] = GetToolFromIdentifier("DEFAULT_DECOR_CLR"); @@ -465,40 +454,34 @@ void GameModel::BuildFavoritesMenu() void GameModel::BuildBrushList() { - bool hasStoredRadius = false; - ui::Point radius = ui::Point(0, 0); + ui::Point radius{ 4, 4 }; if (brushList.size()) - { radius = brushList[currentBrush]->GetRadius(); - hasStoredRadius = true; - } brushList.clear(); - brushList.push_back(new EllipseBrush(ui::Point(4, 4), perfectCircle)); - brushList.push_back(new Brush(ui::Point(4, 4))); - brushList.push_back(new TriangleBrush(ui::Point(4, 4))); + brushList.push_back(std::make_unique(perfectCircle)); + brushList.push_back(std::make_unique()); + brushList.push_back(std::make_unique()); //Load more from brushes folder - std::vector brushFiles = Platform::DirectorySearch(BRUSH_DIR, "", { ".ptb" }); - for (size_t i = 0; i < brushFiles.size(); i++) + for (ByteString brushFile : Platform::DirectorySearch(BRUSH_DIR, "", { ".ptb" })) { std::vector brushData; - if (!Platform::ReadFile(brushData, BRUSH_DIR + ByteString(PATH_SEP) + brushFiles[i])) + if (!Platform::ReadFile(brushData, ByteString::Build(BRUSH_DIR, PATH_SEP_CHAR, brushFile))) { - std::cout << "Brushes: Skipping " << brushFiles[i] << ". Could not open" << std::endl; + std::cout << "Brushes: Skipping " << brushFile << ". Could not open" << std::endl; continue; } - auto dimension = size_t(std::sqrt(float(brushData.size()))); + auto dimension = size_t(std::sqrt(brushData.size())); if (dimension * dimension != brushData.size()) { - std::cout << "Brushes: Skipping " << brushFiles[i] << ". Invalid bitmap size" << std::endl; + std::cout << "Brushes: Skipping " << brushFile << ". Invalid bitmap size" << std::endl; continue; } - brushList.push_back(new BitmapBrush(reinterpret_cast(&brushData[0]), ui::Point(dimension, dimension))); + brushList.push_back(std::make_unique(ui::Point(dimension, dimension), reinterpret_cast(brushData.data()))); } - if (hasStoredRadius && (size_t)currentBrush < brushList.size()) - brushList[currentBrush]->SetRadius(radius); + brushList[currentBrush]->SetRadius(radius); notifyBrushChanged(); } @@ -508,7 +491,7 @@ Tool *GameModel::GetToolFromIdentifier(ByteString const &identifier) { for (auto *tool : menu->GetToolList()) { - if (identifier == tool->GetIdentifier()) + if (identifier == tool->Identifier) { return tool; } @@ -516,7 +499,7 @@ Tool *GameModel::GetToolFromIdentifier(ByteString const &identifier) } for (auto *extra : extraElementTools) { - if (identifier == extra->GetIdentifier()) + if (identifier == extra->Identifier) { return extra; } @@ -794,34 +777,52 @@ unsigned int GameModel::GetUndoHistoryLimit() void GameModel::SetUndoHistoryLimit(unsigned int undoHistoryLimit_) { undoHistoryLimit = undoHistoryLimit_; - Client::Ref().SetPref("Simulation.UndoHistoryLimit", undoHistoryLimit); + GlobalPrefs::Ref().Set("Simulation.UndoHistoryLimit", undoHistoryLimit); } void GameModel::SetVote(int direction) { - if(currentSave) + queuedVote = direction; +} + +void GameModel::Tick() +{ + if (execVoteRequest && execVoteRequest->CheckDone()) { - RequestStatus status = Client::Ref().ExecVote(currentSave->GetID(), direction); - if(status == RequestOkay) + try { - currentSave->vote = direction; + execVoteRequest->Finish(); + currentSave->vote = execVoteRequest->Direction(); notifySaveChanged(); } - else + catch (const http::RequestError &ex) { - throw GameModelException("Could not vote: "+Client::Ref().GetLastError()); + new ErrorMessage("Error while voting", ByteString(ex.what()).FromUtf8()); } + execVoteRequest.reset(); + } + if (!execVoteRequest && queuedVote) + { + if (currentSave) + { + execVoteRequest = std::make_unique(currentSave->GetID(), *queuedVote); + execVoteRequest->Start(); + } + queuedVote.reset(); } } -Brush * GameModel::GetBrush() +Brush &GameModel::GetBrush() { - return brushList[currentBrush]; + return *brushList[currentBrush]; } -std::vector GameModel::GetBrushList() +Brush *GameModel::GetBrushByID(int i) { - return brushList; + if (i >= 0 && i < (int)brushList.size()) + return brushList[i].get(); + else + return nullptr; } int GameModel::GetBrushID() @@ -910,7 +911,7 @@ Tool * GameModel::GetElementTool(int elementID) { for(std::vector::iterator iter = elementTools.begin(), end = elementTools.end(); iter != end; ++iter) { - if((*iter)->GetToolID() == elementID) + if((*iter)->ToolID == elementID) return *iter; } return NULL; @@ -937,115 +938,113 @@ std::vector GameModel::GetMenuList() return menuList; } -SaveInfo * GameModel::GetSave() +SaveInfo *GameModel::GetSave() // non-owning { - return currentSave; + return currentSave.get(); } -void GameModel::SetSave(SaveInfo * newSave, bool invertIncludePressure) +std::unique_ptr GameModel::TakeSave() { - if(currentSave != newSave) - { - delete currentSave; - if(newSave == NULL) - currentSave = NULL; - else - currentSave = new SaveInfo(*newSave); - } - delete currentFile; - currentFile = NULL; + // we don't notify listeners because we'll get a new save soon anyway + return std::move(currentSave); +} - if(currentSave && currentSave->GetGameSave()) +void GameModel::SaveToSimParameters(const GameSave &saveData) +{ + SetPaused(saveData.paused | GetPaused()); + sim->gravityMode = saveData.gravityMode; + sim->customGravityX = saveData.customGravityX; + sim->customGravityY = saveData.customGravityY; + sim->air->airMode = saveData.airMode; + sim->air->ambientAirTemp = saveData.ambientAirTemp; + sim->edgeMode = saveData.edgeMode; + sim->legacy_enable = saveData.legacyEnable; + sim->water_equal_test = saveData.waterEEnabled; + sim->aheat_enable = saveData.aheatEnable; + if (saveData.gravityEnable && !sim->grav->IsEnabled()) { - GameSave * saveData = currentSave->GetGameSave(); - SetPaused(saveData->paused | GetPaused()); - sim->gravityMode = saveData->gravityMode; - sim->customGravityX = saveData->customGravityX; - sim->customGravityY = saveData->customGravityY; - sim->air->airMode = saveData->airMode; - sim->air->ambientAirTemp = saveData->ambientAirTemp; - sim->edgeMode = saveData->edgeMode; - sim->legacy_enable = saveData->legacyEnable; - sim->water_equal_test = saveData->waterEEnabled; - sim->aheat_enable = saveData->aheatEnable; - if(saveData->gravityEnable) - sim->grav->start_grav_async(); - else - sim->grav->stop_grav_async(); + sim->grav->start_grav_async(); + } + else if (!saveData.gravityEnable && sim->grav->IsEnabled()) + { + sim->grav->stop_grav_async(); + } + sim->frameCount = saveData.frameCount; + if (saveData.hasRngState) + { + sim->rng.state(saveData.rngState); + } + else + { + sim->rng = RNG(); + } + sim->ensureDeterminism = saveData.ensureDeterminism; +} + +void GameModel::SetSave(std::unique_ptr newSave, bool invertIncludePressure) +{ + currentSave = std::move(newSave); + currentFile.reset(); + + if (currentSave && currentSave->GetGameSave()) + { + auto *saveData = currentSave->GetGameSave(); + SaveToSimParameters(*saveData); sim->clear_sim(); ren->ClearAccumulation(); - if (!sim->Load(saveData, !invertIncludePressure)) + sim->Load(saveData, !invertIncludePressure, { 0, 0 }); + // This save was created before logging existed + // Add in the correct info + if (saveData->authors.size() == 0) { - // This save was created before logging existed - // Add in the correct info - if (saveData->authors.size() == 0) - { - saveData->authors["type"] = "save"; - saveData->authors["id"] = newSave->id; - saveData->authors["username"] = newSave->userName; - saveData->authors["title"] = newSave->name.ToUtf8(); - saveData->authors["description"] = newSave->Description.ToUtf8(); - saveData->authors["published"] = (int)newSave->Published; - saveData->authors["date"] = newSave->updatedDate; - } - // This save was probably just created, and we didn't know the ID when creating it - // Update with the proper ID - else if (saveData->authors.get("id", -1) == 0 || saveData->authors.get("id", -1) == -1) - { - saveData->authors["id"] = newSave->id; - } - Client::Ref().OverwriteAuthorInfo(saveData->authors); + auto gameSave = currentSave->TakeGameSave(); + gameSave->authors["type"] = "save"; + gameSave->authors["id"] = currentSave->id; + gameSave->authors["username"] = currentSave->userName; + gameSave->authors["title"] = currentSave->name.ToUtf8(); + gameSave->authors["description"] = currentSave->Description.ToUtf8(); + gameSave->authors["published"] = (int)currentSave->Published; + gameSave->authors["date"] = (Json::Value::UInt64)currentSave->updatedDate; + currentSave->SetGameSave(std::move(gameSave)); } + // This save was probably just created, and we didn't know the ID when creating it + // Update with the proper ID + else if (saveData->authors.get("id", -1) == 0 || saveData->authors.get("id", -1) == -1) + { + auto gameSave = currentSave->TakeGameSave(); + gameSave->authors["id"] = currentSave->id; + currentSave->SetGameSave(std::move(gameSave)); + } + Client::Ref().OverwriteAuthorInfo(saveData->authors); } notifySaveChanged(); UpdateQuickOptions(); } -SaveFile * GameModel::GetSaveFile() +const SaveFile *GameModel::GetSaveFile() const { - return currentFile; + return currentFile.get(); } -void GameModel::SetSaveFile(SaveFile * newSave, bool invertIncludePressure) +std::unique_ptr GameModel::TakeSaveFile() { - if(currentFile != newSave) - { - delete currentFile; - if(newSave == NULL) - currentFile = NULL; - else - currentFile = new SaveFile(*newSave); - } - delete currentSave; - currentSave = NULL; + // we don't notify listeners because we'll get a new save soon anyway + return std::move(currentFile); +} - if(newSave && newSave->GetGameSave()) +void GameModel::SetSaveFile(std::unique_ptr newSave, bool invertIncludePressure) +{ + currentFile = std::move(newSave); + currentSave.reset(); + + if (currentFile && currentFile->GetGameSave()) { - GameSave * saveData = newSave->GetGameSave(); - SetPaused(saveData->paused | GetPaused()); - sim->gravityMode = saveData->gravityMode; - sim->customGravityX = saveData->customGravityX; - sim->customGravityY = saveData->customGravityY; - sim->air->airMode = saveData->airMode; - sim->air->ambientAirTemp = saveData->ambientAirTemp; - sim->edgeMode = saveData->edgeMode; - sim->legacy_enable = saveData->legacyEnable; - sim->water_equal_test = saveData->waterEEnabled; - sim->aheat_enable = saveData->aheatEnable; - if(saveData->gravityEnable && !sim->grav->IsEnabled()) - { - sim->grav->start_grav_async(); - } - else if(!saveData->gravityEnable && sim->grav->IsEnabled()) - { - sim->grav->stop_grav_async(); - } + auto *saveData = currentFile->GetGameSave(); + SaveToSimParameters(*saveData); sim->clear_sim(); ren->ClearAccumulation(); - if (!sim->Load(saveData, !invertIncludePressure)) - { - Client::Ref().OverwriteAuthorInfo(saveData->authors); - } + sim->Load(saveData, !invertIncludePressure, { 0, 0 }); + Client::Ref().OverwriteAuthorInfo(saveData->authors); } notifySaveChanged(); @@ -1214,13 +1213,8 @@ void GameModel::SetColourSelectorColour(ui::Colour colour_) colour = colour_; std::vector tools = GetMenuList()[SC_DECO]->GetToolList(); - for (size_t i = 0; i < tools.size(); i++) - { - ((DecorationTool*)tools[i])->Red = colour.Red; - ((DecorationTool*)tools[i])->Green = colour.Green; - ((DecorationTool*)tools[i])->Blue = colour.Blue; - ((DecorationTool*)tools[i])->Alpha = colour.Alpha; - } + for (auto tool : tools) + static_cast(tool)->Colour = colour; notifyColourSelectorColourChanged(); } @@ -1239,12 +1233,10 @@ void GameModel::SetUser(User user) void GameModel::SetPaused(bool pauseState) { - if (!pauseState && sim->debug_currentParticle > 0) + if (!pauseState && sim->debug_nextToUpdate > 0) { - String logmessage = String::Build("Updated particles from #", sim->debug_currentParticle, " to end due to unpause"); - sim->UpdateParticles(sim->debug_currentParticle, NPART - 1); - sim->AfterSim(); - sim->debug_currentParticle = 0; + String logmessage = String::Build("Updated particles from #", sim->debug_nextToUpdate, " to end due to unpause"); + UpdateUpTo(NPART); Log(logmessage, false); } @@ -1355,33 +1347,36 @@ void GameModel::ClearSimulation() UpdateQuickOptions(); } -void GameModel::SetPlaceSave(GameSave * save) +void GameModel::SetPlaceSave(std::unique_ptr save) { - if (save != placeSave) - { - delete placeSave; - if (save) - placeSave = new GameSave(*save); - else - placeSave = NULL; - } + transformedPlaceSave.reset(); + placeSave = std::move(save); notifyPlaceSaveChanged(); } -void GameModel::SetClipboard(GameSave * save) +void GameModel::TransformPlaceSave(Mat2 transform, Vec2 nudge) { - delete clipboard; - clipboard = save; + if (placeSave) + { + transformedPlaceSave = std::make_unique(*placeSave); + transformedPlaceSave->Transform(transform, nudge); + } + notifyTransformedPlaceSaveChanged(); } -GameSave * GameModel::GetClipboard() +void GameModel::SetClipboard(std::unique_ptr save) { - return clipboard; + clipboard = std::move(save); } -GameSave * GameModel::GetPlaceSave() +const GameSave *GameModel::GetClipboard() const { - return placeSave; + return clipboard.get(); +} + +const GameSave *GameModel::GetTransformedPlaceSave() const +{ + return transformedPlaceSave.get(); } void GameModel::Log(String message, bool printToFile) @@ -1582,6 +1577,14 @@ void GameModel::notifyPlaceSaveChanged() } } +void GameModel::notifyTransformedPlaceSaveChanged() +{ + for (size_t i = 0; i < observers.size(); i++) + { + observers[i]->NotifyTransformedPlaceSaveChanged(this); + } +} + void GameModel::notifyLogChanged(String entry) { for (size_t i = 0; i < observers.size(); i++) @@ -1654,17 +1657,58 @@ void GameModel::SetPerfectCircle(bool perfectCircle) bool GameModel::RemoveCustomGOLType(const ByteString &identifier) { bool removedAny = false; - auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types"); - Json::Value newCustomGOLTypes(Json::arrayValue); + auto &prefs = GlobalPrefs::Ref(); + auto customGOLTypes = prefs.Get("CustomGOL.Types", std::vector{}); + std::vector newCustomGOLTypes; for (auto gol : customGOLTypes) { auto parts = gol.PartitionBy(' '); if (parts.size() && "DEFAULT_PT_LIFECUST_" + parts[0] == identifier) removedAny = true; else - newCustomGOLTypes.append(gol); + newCustomGOLTypes.push_back(gol); + } + if (removedAny) + { + prefs.Set("CustomGOL.Types", newCustomGOLTypes); } - Client::Ref().SetPref("CustomGOL.Types", newCustomGOLTypes); BuildMenus(); return removedAny; } + +void GameModel::UpdateUpTo(int upTo) +{ + if (upTo < sim->debug_nextToUpdate) + { + upTo = NPART; + } + if (sim->debug_nextToUpdate == 0) + { + BeforeSim(); + } + sim->UpdateParticles(sim->debug_nextToUpdate, upTo); + if (upTo < NPART) + { + sim->debug_nextToUpdate = upTo; + } + else + { + AfterSim(); + sim->debug_nextToUpdate = 0; + } +} + +void GameModel::BeforeSim() +{ + if (!sim->sys_pause || sim->framerender) + { + commandInterface->HandleEvent(BeforeSimEvent{}); + } + sim->BeforeSim(); +} + +void GameModel::AfterSim() +{ + sim->AfterSim(); + commandInterface->HandleEvent(AfterSimEvent{}); +} diff --git a/src/gui/game/GameModel.h b/src/gui/game/GameModel.h index d3e00e6ba..2fc8a187f 100644 --- a/src/gui/game/GameModel.h +++ b/src/gui/game/GameModel.h @@ -1,14 +1,11 @@ -#ifndef GAMEMODEL_H -#define GAMEMODEL_H -#include "Config.h" - -#include -#include -#include - +#pragma once #include "gui/interface/Colour.h" #include "client/User.h" #include "gui/interface/Point.h" +#include +#include +#include +#include class Menu; class Tool; @@ -25,6 +22,11 @@ class Snapshot; struct SnapshotDelta; class GameSave; +namespace http +{ + class ExecVoteRequest; +}; + class ToolSelection { public: @@ -44,12 +46,15 @@ struct HistoryEntry class GameModel { + std::unique_ptr execVoteRequest; + private: std::vector notifications; //int clipboardSize; //unsigned char * clipboardData; - GameSave * clipboard; - GameSave * placeSave; + std::unique_ptr clipboard; + std::unique_ptr placeSave; + std::unique_ptr transformedPlaceSave; std::deque consoleLog; std::vector observers; std::vector toolList; @@ -65,9 +70,9 @@ private: std::vector quickOptions; int activeMenu; int currentBrush; - std::vector brushList; - SaveInfo * currentSave; - SaveFile * currentFile; + std::vector> brushList; + std::unique_ptr currentSave; + std::unique_ptr currentFile; Tool * lastTool; Tool ** activeTools; Tool * decoToolset[4]; @@ -108,6 +113,7 @@ private: void notifyZoomChanged(); void notifyClipboardChanged(); void notifyPlaceSaveChanged(); + void notifyTransformedPlaceSaveChanged(); void notifyColourSelectorColourChanged(); void notifyColourSelectorVisibilityChanged(); void notifyColourPresetsChanged(); @@ -118,10 +124,17 @@ private: void notifyToolTipChanged(); void notifyQuickOptionsChanged(); void notifyLastToolChanged(); + + void SaveToSimParameters(const GameSave &saveData); + + std::optional queuedVote; + public: GameModel(); ~GameModel(); + void Tick(); + void SetEdgeMode(int edgeMode); int GetEdgeMode(); void SetTemperatureScale(int temperatureScale); @@ -179,16 +192,18 @@ public: std::vector GetToolList(); std::vector GetUnlistedTools(); - Brush * GetBrush(); - std::vector GetBrushList(); + Brush &GetBrush(); + Brush *GetBrushByID(int i); int GetBrushID(); void SetBrushID(int i); void SetVote(int direction); - SaveInfo * GetSave(); - SaveFile * GetSaveFile(); - void SetSave(SaveInfo * newSave, bool invertIncludePressure); - void SetSaveFile(SaveFile * newSave, bool invertIncludePressure); + SaveInfo *GetSave(); // non-owning + std::unique_ptr TakeSave(); + const SaveFile *GetSaveFile() const; + std::unique_ptr TakeSaveFile(); + void SetSave(std::unique_ptr newSave, bool invertIncludePressure); + void SetSaveFile(std::unique_ptr newSave, bool invertIncludePressure); void AddObserver(GameView * observer); void SetPaused(bool pauseState); @@ -224,12 +239,14 @@ public: ui::Point AdjustZoomCoords(ui::Point position); void SetZoomWindowPosition(ui::Point position); ui::Point GetZoomWindowPosition(); - void SetClipboard(GameSave * save); - void SetPlaceSave(GameSave * save); + void SetClipboard(std::unique_ptr save); + void SetPlaceSave(std::unique_ptr save); + void TransformPlaceSave(Mat2 transform, Vec2 nudge); void Log(String message, bool printToFile); std::deque GetLog(); - GameSave * GetClipboard(); - GameSave * GetPlaceSave(); + const GameSave *GetClipboard() const; + const GameSave *GetPlaceSave() const; + const GameSave *GetTransformedPlaceSave() const; bool GetMouseClickRequired(); void SetMouseClickRequired(bool mouseClickRequired); bool GetIncludePressure(); @@ -248,6 +265,8 @@ public: ByteString SelectNextIdentifier; int SelectNextTool; -}; -#endif // GAMEMODEL_H + void UpdateUpTo(int upTo); + void BeforeSim(); + void AfterSim(); +}; diff --git a/src/gui/game/GameModelException.h b/src/gui/game/GameModelException.h index b4b2393a2..db3eef50d 100644 --- a/src/gui/game/GameModelException.h +++ b/src/gui/game/GameModelException.h @@ -1,6 +1,4 @@ -#ifndef GAMEMODELEXCEPTION_H_ -#define GAMEMODELEXCEPTION_H_ - +#pragma once #include "common/String.h" #include @@ -15,5 +13,3 @@ public: } ~GameModelException() throw() {} }; - -#endif /* GAMEMODELEXCEPTION_H_ */ diff --git a/src/gui/game/GameView.cpp b/src/gui/game/GameView.cpp index 8e23060a6..7e27acd36 100644 --- a/src/gui/game/GameView.cpp +++ b/src/gui/game/GameView.cpp @@ -1,7 +1,6 @@ #include "GameView.h" #include "Brush.h" -#include "Config.h" #include "DecorationTool.h" #include "Favorite.h" #include "Format.h" @@ -18,7 +17,8 @@ #include "client/SaveInfo.h" #include "client/SaveFile.h" #include "client/Client.h" -#include "common/Platform.h" +#include "client/GameSave.h" +#include "common/platform/Platform.h" #include "graphics/Graphics.h" #include "graphics/Renderer.h" #include "gui/Style.h" @@ -32,11 +32,13 @@ #include "gui/dialogues/InformationMessage.h" #include "gui/interface/Button.h" #include "gui/interface/Colour.h" -#include "gui/interface/Keys.h" #include "gui/interface/Engine.h" +#include "Config.h" #include +#include #include +#include #ifdef GetUserName # undef GetUserName // dammit windows @@ -153,7 +155,7 @@ public: drawn = true; if(showSplit) - g->draw_line(splitPosition+screenPos.X, screenPos.Y+1, splitPosition+screenPos.X, screenPos.Y+Size.Y-2, 180, 180, 180, 255); + g->DrawLine(screenPos + Vec2{ splitPosition, 1 }, screenPos + Vec2{ splitPosition, Size.Y-2 }, 0xB4B4B4_rgb); } }; @@ -191,7 +193,7 @@ GameView::GameView(): buttonTip(""), isButtonTipFadingIn(false), introText(2048), - introTextMessage(ByteString(introTextData).FromUtf8()), + introTextMessage(IntroText().FromUtf8()), doScreenshot(false), screenshotIndex(1), @@ -212,9 +214,7 @@ GameView::GameView(): selectPoint1(0, 0), selectPoint2(0, 0), currentMouse(0, 0), - mousePosition(0, 0), - placeSaveThumb(NULL), - placeSaveOffset(0, 0) + mousePosition(0, 0) { int currentX = 1; @@ -344,8 +344,6 @@ GameView::~GameView() } } - - delete placeSaveThumb; } class GameView::OptionListener: public QuickOptionListener @@ -518,15 +516,15 @@ void GameView::NotifyActiveToolsChanged(GameModel * sender) toolButtons[i]->SetSelectionState(-1); } - decoBrush = sender->GetActiveTool(0)->GetIdentifier().BeginsWith("DEFAULT_DECOR_"); + decoBrush = sender->GetActiveTool(0)->Identifier.BeginsWith("DEFAULT_DECOR_"); if (sender->GetRenderer()->findingElement) { Tool *active = sender->GetActiveTool(0); - if (!active->GetIdentifier().Contains("_PT_")) + if (!active->Identifier.Contains("_PT_")) ren->findingElement = 0; else - ren->findingElement = sender->GetActiveTool(0)->GetToolID(); + ren->findingElement = sender->GetActiveTool(0)->ToolID; } } @@ -534,8 +532,8 @@ void GameView::NotifyLastToolChanged(GameModel * sender) { if (sender->GetLastTool()) { - wallBrush = sender->GetLastTool()->GetBlocky(); - toolBrush = sender->GetLastTool()->GetIdentifier().BeginsWith("DEFAULT_TOOL_"); + wallBrush = sender->GetLastTool()->Blocky; + toolBrush = sender->GetLastTool()->Identifier.BeginsWith("DEFAULT_TOOL_"); } } @@ -563,22 +561,19 @@ void GameView::NotifyToolListChanged(GameModel * sender) for (size_t i = 0; i < toolList.size(); i++) { auto *tool = toolList[i]; - VideoBuffer * tempTexture = tool->GetTexture(26, 14); + auto tempTexture = tool->GetTexture(Vec2(26, 14)); ToolButton * tempButton; //get decotool texture manually, since it changes depending on it's own color if (sender->GetActiveMenu() == SC_DECO) - tempTexture = ((DecorationTool*)tool)->GetIcon(tool->GetToolID(), 26, 14); + tempTexture = ((DecorationTool*)tool)->GetIcon(tool->ToolID, Vec2(26, 14)); - if(tempTexture) - tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), "", tool->GetIdentifier(), tool->GetDescription()); + if (tempTexture) + tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), "", tool->Identifier, tool->Description); else - tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), tool->GetName(), tool->GetIdentifier(), tool->GetDescription()); + tempButton = new ToolButton(ui::Point(currentX, YRES+1), ui::Point(30, 18), tool->Name, tool->Identifier, tool->Description); - tempButton->clipRectX = 1; - tempButton->clipRectY = YRES + 1; - tempButton->clipRectW = XRES - 1; - tempButton->clipRectH = 18; + tempButton->ClipRect = RectSized(Vec2(1, RES.Y + 1), Vec2(RES.X - 1, 18)); //currentY -= 17; currentX -= 31; @@ -589,19 +584,19 @@ void GameView::NotifyToolListChanged(GameModel * sender) { if (tempButton->GetSelectionState() == 0) { - if (Favorite::Ref().IsFavorite(tool->GetIdentifier())) + if (Favorite::Ref().IsFavorite(tool->Identifier)) { - Favorite::Ref().RemoveFavorite(tool->GetIdentifier()); + Favorite::Ref().RemoveFavorite(tool->Identifier); } else { - Favorite::Ref().AddFavorite(tool->GetIdentifier()); + Favorite::Ref().AddFavorite(tool->Identifier); } c->RebuildFavoritesMenu(); } else if (tempButton->GetSelectionState() == 1) { - auto identifier = tool->GetIdentifier(); + auto identifier = tool->Identifier; if (Favorite::Ref().IsFavorite(identifier)) { Favorite::Ref().RemoveFavorite(identifier); @@ -620,7 +615,7 @@ void GameView::NotifyToolListChanged(GameModel * sender) { if (CtrlBehaviour() && AltBehaviour() && !ShiftBehaviour()) { - if (tool->GetIdentifier().Contains("_PT_")) + if (tool->Identifier.Contains("_PT_")) { tempButton->SetSelectionState(3); } @@ -631,10 +626,9 @@ void GameView::NotifyToolListChanged(GameModel * sender) } } }); - tempButton->Appearance.SetTexture(tempTexture); - delete tempTexture; + tempButton->Appearance.SetTexture(std::move(tempTexture)); - tempButton->Appearance.BackgroundInactive = ui::Colour(toolList[i]->colRed, toolList[i]->colGreen, toolList[i]->colBlue); + tempButton->Appearance.BackgroundInactive = toolList[i]->Colour.WithAlpha(0xFF); if(sender->GetActiveTool(0) == toolList[i]) { @@ -806,11 +800,13 @@ void GameView::NotifySaveChanged(GameModel * sender) { upVoteButton->Appearance.BackgroundHover = (ui::Colour(20, 128, 30, 255)); upVoteButton->Appearance.BackgroundInactive = (ui::Colour(0, 108, 10, 255)); + upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 108, 10, 255)); } else { upVoteButton->Appearance.BackgroundHover = (ui::Colour(20, 20, 20)); upVoteButton->Appearance.BackgroundInactive = (ui::Colour(0, 0, 0)); + upVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0)); } downVoteButton->Enabled = upVoteButton->Enabled; @@ -818,11 +814,13 @@ void GameView::NotifySaveChanged(GameModel * sender) { downVoteButton->Appearance.BackgroundHover = (ui::Colour(128, 20, 30, 255)); downVoteButton->Appearance.BackgroundInactive = (ui::Colour(108, 0, 10, 255)); + downVoteButton->Appearance.BackgroundDisabled = (ui::Colour(108, 0, 10, 255)); } else { downVoteButton->Appearance.BackgroundHover = (ui::Colour(20, 20, 20)); downVoteButton->Appearance.BackgroundInactive = (ui::Colour(0, 0, 0)); + downVoteButton->Appearance.BackgroundDisabled = (ui::Colour(0, 0, 0)); } if (sender->GetUser().UserID) @@ -914,7 +912,7 @@ void GameView::NotifySaveChanged(GameModel * sender) void GameView::NotifyBrushChanged(GameModel * sender) { - activeBrush = sender->GetBrush(); + activeBrush = &sender->GetBrush(); } ByteString GameView::TakeScreenshot(int captureUI, int fileType) @@ -954,7 +952,7 @@ ByteString GameView::TakeScreenshot(int captureUI, int fileType) // We should be able to simply use SDL_PIXELFORMAT_XRGB8888 here with a bit depth of 32 to convert RGBA data to RGB data, // and save the resulting surface directly. However, ubuntu-18.04 ships SDL2 so old that it doesn't have // SDL_PIXELFORMAT_XRGB8888, so we first create an RGBA surface and then convert it. - auto *rgbaSurface = SDL_CreateRGBSurfaceWithFormatFrom(screenshot->Buffer, screenshot->Width, screenshot->Height, 32, screenshot->Width * sizeof(pixel), SDL_PIXELFORMAT_ARGB8888); + auto *rgbaSurface = SDL_CreateRGBSurfaceWithFormatFrom(screenshot->Data(), screenshot->Size().X, screenshot->Size().Y, 32, screenshot->Size().X * sizeof(pixel), SDL_PIXELFORMAT_ARGB8888); auto *rgbSurface = SDL_ConvertSurfaceFormat(rgbaSurface, SDL_PIXELFORMAT_RGB888, 0); if (!rgbSurface || SDL_SaveBMP(rgbSurface, filename.c_str())) { @@ -967,7 +965,7 @@ ByteString GameView::TakeScreenshot(int captureUI, int fileType) else if (fileType == 2) { filename += ".ppm"; - if (!Platform::WriteFile(format::VideoBufferToPPM(*screenshot), filename)) + if (!Platform::WriteFile(screenshot->ToPPM(), filename)) { filename = ""; } @@ -975,10 +973,13 @@ ByteString GameView::TakeScreenshot(int captureUI, int fileType) else { filename += ".png"; - if (!screenshot->WritePNG(filename)) + if (auto data = screenshot->ToPNG()) { - filename = ""; + if (!Platform::WriteFile(*data, filename)) + filename = ""; } + else + filename = ""; } return filename; @@ -1000,7 +1001,7 @@ int GameView::Record(bool record) time_t startTime = time(NULL); recordingFolder = startTime; Platform::MakeDirectory("recordings"); - Platform::MakeDirectory(ByteString::Build("recordings", PATH_SEP, recordingFolder).c_str()); + Platform::MakeDirectory(ByteString::Build("recordings", PATH_SEP_CHAR, recordingFolder)); recording = true; recordingIndex = 0; } @@ -1152,8 +1153,8 @@ void GameView::OnMouseDown(int x, int y, unsigned button) return; Tool *lastTool = c->GetActiveTool(toolIndex); c->SetLastTool(lastTool); - windTool = lastTool->GetIdentifier() == "DEFAULT_UI_WIND"; - decoBrush = lastTool->GetIdentifier().BeginsWith("DEFAULT_DECOR_"); + windTool = lastTool->Identifier == "DEFAULT_UI_WIND"; + decoBrush = lastTool->Identifier.BeginsWith("DEFAULT_DECOR_"); UpdateDrawMode(); @@ -1176,6 +1177,20 @@ void GameView::OnMouseDown(int x, int y, unsigned button) } } +Vec2 GameView::PlaceSavePos() const +{ + auto [ trQuoX, trRemX ] = floorDiv(placeSaveTranslate.X, CELL); + auto [ trQuoY, trRemY ] = floorDiv(placeSaveTranslate.Y, CELL); + auto usefulSize = placeSaveThumb->Size(); + if (trRemX) usefulSize.X -= CELL; + if (trRemY) usefulSize.Y -= CELL; + auto cursorCell = (usefulSize - Vec2{ CELL, CELL }) / 2 - Vec2{ trQuoX, trQuoY } * CELL; // stamp coordinates + auto unaligned = selectPoint2 - cursorCell; + auto quoX = floorDiv(unaligned.X, CELL).first; + auto quoY = floorDiv(unaligned.Y, CELL).first; + return { quoX, quoY }; +} + void GameView::OnMouseUp(int x, int y, unsigned button) { currentMouse = ui::Point(x, y); @@ -1196,20 +1211,7 @@ void GameView::OnMouseUp(int x, int y, unsigned button) { if (placeSaveThumb && y <= WINDOWH-BARSIZE) { - int thumbX = selectPoint2.X - ((placeSaveThumb->Width-placeSaveOffset.X)/2); - int thumbY = selectPoint2.Y - ((placeSaveThumb->Height-placeSaveOffset.Y)/2); - - if (thumbX < 0) - thumbX = 0; - if (thumbX+(placeSaveThumb->Width) >= XRES) - thumbX = XRES-placeSaveThumb->Width; - - if (thumbY < 0) - thumbY = 0; - if (thumbY+(placeSaveThumb->Height) >= YRES) - thumbY = YRES-placeSaveThumb->Height; - - c->PlaceSave(ui::Point(thumbX, thumbY)); + c->PlaceSave(PlaceSavePos()); } } else @@ -1288,16 +1290,16 @@ void GameView::ToolTip(ui::Point senderPosition, String toolTip) else if(senderPosition.X > Size.X-BARSIZE)// < Size.Y-(quickOptionButtons.size()+1)*16) { this->toolTip = toolTip; - toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth(toolTip), senderPosition.Y+3); + toolTipPosition = ui::Point(Size.X-27-(Graphics::TextSize(toolTip).X - 1), senderPosition.Y+3); if(toolTipPosition.Y+10 > Size.Y-MENUSIZE) - toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth(toolTip), Size.Y-MENUSIZE-10); + toolTipPosition = ui::Point(Size.X-27-(Graphics::TextSize(toolTip).X - 1), Size.Y-MENUSIZE-10); isToolTipFadingIn = true; } // element tooltips else { this->toolTip = toolTip; - toolTipPosition = ui::Point(Size.X-27-Graphics::textwidth(toolTip), Size.Y-MENUSIZE-10); + toolTipPosition = ui::Point(Size.X-27-(Graphics::TextSize(toolTip).X - 1), Size.Y-MENUSIZE-10); isToolTipFadingIn = true; } } @@ -1316,7 +1318,7 @@ void GameView::OnMouseWheel(int x, int y, int d) } else { - c->AdjustBrushSize(d, false, shiftBehaviour, ctrlBehaviour); + c->AdjustBrushSize(d, false, ctrlBehaviour, shiftBehaviour); } } @@ -1343,16 +1345,16 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, switch (key) { case SDLK_RIGHT: - c->TranslateSave(ui::Point(1, 0)); + TranslateSave({ 1, 0 }); return; case SDLK_LEFT: - c->TranslateSave(ui::Point(-1, 0)); + TranslateSave({ -1, 0 }); return; case SDLK_UP: - c->TranslateSave(ui::Point(0, -1)); + TranslateSave({ 0, -1 }); return; case SDLK_DOWN: - c->TranslateSave(ui::Point(0, 1)); + TranslateSave({ 0, 1 }); return; } if (scan == SDL_SCANCODE_R && !repeat) @@ -1360,17 +1362,17 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, if (ctrl && shift) { //Vertical flip - c->TransformSave(m2d_new(1,0,0,-1)); + TransformSave(Mat2::MirrorY); } else if (!ctrl && shift) { //Horizontal flip - c->TransformSave(m2d_new(-1,0,0,1)); + TransformSave(Mat2::MirrorX); } else { //Rotate 90deg - c->TransformSave(m2d_new(0,1,-1,0)); + TransformSave(Mat2::CCW); } return; } @@ -1424,8 +1426,7 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, c->ReloadSim(); break; case SDL_SCANCODE_A: - if ((Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator - || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin) && ctrl) + if (Client::Ref().GetAuthUser().UserElevation != User::ElevationNone && ctrl) { ByteString authorString = Client::Ref().GetAuthorInfo().toStyledString(); new InformationMessage("Save authorship info", authorString.FromUtf8(), true); @@ -1442,10 +1443,10 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, if (ctrl) { Tool *active = c->GetActiveTool(0); - if (!active->GetIdentifier().Contains("_PT_") || (ren->findingElement == active->GetToolID())) + if (!active->Identifier.Contains("_PT_") || (ren->findingElement == active->ToolID)) ren->findingElement = 0; else - ren->findingElement = active->GetToolID(); + ren->findingElement = active->ToolID; } else c->FrameStep(); @@ -1549,14 +1550,13 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, break; case SDL_SCANCODE_L: { - std::vector stampList = Client::Ref().GetStamps(0, 1); - if (stampList.size()) + auto &stampIDs = Client::Ref().GetStamps(); + if (stampIDs.size()) { - SaveFile *saveFile = Client::Ref().GetStamp(stampList[0]); + auto saveFile = Client::Ref().GetStamp(stampIDs[0]); if (!saveFile || !saveFile->GetGameSave()) break; - c->LoadStamp(saveFile->GetGameSave()); - delete saveFile; + c->LoadStamp(saveFile->TakeGameSave()); selectPoint1 = selectPoint2 = mousePosition; isMouseDown = false; break; @@ -1571,13 +1571,13 @@ void GameView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, if(zoomEnabled && !zoomCursorFixed) c->AdjustZoomSize(1, !alt); else - c->AdjustBrushSize(1, !alt, shiftBehaviour, ctrlBehaviour); + c->AdjustBrushSize(1, !alt, ctrlBehaviour, shiftBehaviour); break; case SDL_SCANCODE_LEFTBRACKET: if(zoomEnabled && !zoomCursorFixed) c->AdjustZoomSize(-1, !alt); else - c->AdjustBrushSize(-1, !alt, shiftBehaviour, ctrlBehaviour); + c->AdjustBrushSize(-1, !alt, ctrlBehaviour, shiftBehaviour); break; case SDL_SCANCODE_I: if(ctrl) @@ -1651,7 +1651,7 @@ void GameView::OnFileDrop(ByteString filename) return; } - SaveFile *saveFile = Client::Ref().LoadSaveFile(filename); + auto saveFile = Client::Ref().LoadSaveFile(filename); if (!saveFile) return; if (saveFile->GetError().length()) @@ -1659,8 +1659,7 @@ void GameView::OnFileDrop(ByteString filename) new ErrorMessage("Error loading save", "Dropped save file could not be loaded: " + saveFile->GetError()); return; } - c->LoadSaveFile(saveFile); - delete saveFile; + c->LoadSaveFile(std::move(saveFile)); // hide the info text if it's not already hidden introText = 0; @@ -1693,7 +1692,7 @@ void GameView::OnTick(float dt) ui::Point drawPoint2 = currentMouse; if (altBehaviour) drawPoint2 = lineSnapCoords(c->PointTranslate(drawPoint1), currentMouse); - c->DrawLine(toolIndex, c->PointTranslate(drawPoint1), drawPoint2); + c->DrawLine(toolIndex, c->PointTranslate(drawPoint1), c->PointTranslateNoClamp(drawPoint2)); } } @@ -1847,19 +1846,16 @@ void GameView::DoDraw() 255, 195, 145, 103, 69, 42, 23, 10, 3 } }; auto *g = GetGraphics(); - for (auto x = 0U; x < fadeout.size(); ++x) + for (auto x = 0; x < int(fadeout.size()); ++x) { - g->draw_line(x, YRES + 1, x, YRES + 18, 0, 0, 0, fadeout[x]); - g->draw_line(XRES - x, YRES + 1, XRES - x, YRES + 18, 0, 0, 0, fadeout[x]); + g->BlendLine({ x, YRES + 1 }, { x, YRES + 18 }, 0x000000_rgb .WithAlpha(fadeout[x])); + g->BlendLine({ XRES - x, YRES + 1 }, { XRES - x, YRES + 18 }, 0x000000_rgb .WithAlpha(fadeout[x])); } c->Tick(); { - int x = 0; - int y = 0; - int w = WINDOWW; - int h = WINDOWH; - g->SetClipRect(x, y, w, h); // reset any nonsense cliprect Lua left configured + auto rect = g->Size().OriginRect(); + g->SwapClipRect(rect); // reset any nonsense cliprect Lua left configured } } @@ -1877,7 +1873,7 @@ void GameView::NotifyNotificationsChanged(GameModel * sender) int currentY = YRES-23; for (auto *notification : notifications) { - int width = (Graphics::textwidth(notification->Message))+8; + int width = Graphics::TextSize(notification->Message).X + 7; ui::Button * tempButton = new ui::Button(ui::Point(XRES-width-22, currentY), ui::Point(width, 15), notification->Message); tempButton->SetActionCallback({ [notification] { notification->Action(); @@ -1922,17 +1918,42 @@ void GameView::NotifyLogChanged(GameModel * sender, String entry) void GameView::NotifyPlaceSaveChanged(GameModel * sender) { - delete placeSaveThumb; - placeSaveOffset = ui::Point(0, 0); - if(sender->GetPlaceSave()) + placeSaveTransform = Mat2::Identity; + placeSaveTranslate = Vec2::Zero; + ApplyTransformPlaceSave(); +} + +void GameView::TranslateSave(Vec2 addToTranslate) +{ + placeSaveTranslate += addToTranslate; + ApplyTransformPlaceSave(); +} + +void GameView::TransformSave(Mat2 mulToTransform) +{ + placeSaveTranslate = Vec2::Zero; // reset offset + placeSaveTransform = mulToTransform * placeSaveTransform; + ApplyTransformPlaceSave(); +} + +void GameView::ApplyTransformPlaceSave() +{ + auto remX = floorDiv(placeSaveTranslate.X, CELL).second; + auto remY = floorDiv(placeSaveTranslate.Y, CELL).second; + c->TransformPlaceSave(placeSaveTransform, { remX, remY }); +} + +void GameView::NotifyTransformedPlaceSaveChanged(GameModel *sender) +{ + if (sender->GetTransformedPlaceSave()) { - placeSaveThumb = SaveRenderer::Ref().Render(sender->GetPlaceSave(), true, true, sender->GetRenderer()); + placeSaveThumb = SaveRenderer::Ref().Render(sender->GetTransformedPlaceSave(), true, true, sender->GetRenderer()); selectMode = PlaceSave; selectPoint2 = mousePosition; } else { - placeSaveThumb = NULL; + placeSaveThumb.reset(); selectMode = SelectNone; } } @@ -2070,12 +2091,12 @@ void GameView::OnDraw() Graphics * g = GetGraphics(); if (ren) { - ren->clearScreen(1.0f); + ren->clearScreen(); ren->RenderBegin(); - ren->SetSample(c->PointTranslate(currentMouse).X, c->PointTranslate(currentMouse).Y); + ren->SetSample(c->PointTranslate(currentMouse)); if (showBrush && selectMode == SelectNone && (!zoomEnabled || zoomCursorFixed) && activeBrush && (isMouseDown || (currentMouse.X >= 0 && currentMouse.X < XRES && currentMouse.Y >= 0 && currentMouse.Y < YRES))) { - ui::Point finalCurrentMouse = c->PointTranslate(currentMouse); + ui::Point finalCurrentMouse = windTool ? c->PointTranslateNoClamp(currentMouse) : c->PointTranslate(currentMouse); ui::Point initialDrawPoint = drawPoint1; if (wallBrush) @@ -2122,11 +2143,12 @@ void GameView::OnDraw() if (wallBrush) { ui::Point finalBrushRadius = c->NormaliseBlockCoord(activeBrush->GetRadius()); - ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y-finalBrushRadius.Y, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y-finalBrushRadius.Y); - ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-1, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-1); - - ren->xor_line(finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y-finalBrushRadius.Y+1, finalCurrentMouse.X-finalBrushRadius.X, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-2); - ren->xor_line(finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y-finalBrushRadius.Y+1, finalCurrentMouse.X+finalBrushRadius.X+CELL-1, finalCurrentMouse.Y+finalBrushRadius.Y+CELL-2); + auto topLeft = finalCurrentMouse - finalBrushRadius; + auto bottomRight = finalCurrentMouse + finalBrushRadius + Vec2{ CELL - 1, CELL - 1 }; + ren->XorLine({ topLeft.X, topLeft.Y }, { bottomRight.X, topLeft.Y }); + ren->XorLine({ topLeft.X, bottomRight.Y }, { bottomRight.X, bottomRight.Y }); + ren->XorLine({ topLeft.X, topLeft.Y + 1 }, { topLeft.X, bottomRight.Y - 1 }); // offset by 1 so the corners don't get xor'd twice + ren->XorLine({ bottomRight.X, topLeft.Y + 1 }, { bottomRight.X, bottomRight.Y - 1 }); // offset by 1 so the corners don't get xor'd twice } else { @@ -2141,31 +2163,16 @@ void GameView::OnDraw() { if(placeSaveThumb && selectPoint2.X!=-1) { - int thumbX = selectPoint2.X - ((placeSaveThumb->Width-placeSaveOffset.X)/2) + CELL/2; - int thumbY = selectPoint2.Y - ((placeSaveThumb->Height-placeSaveOffset.Y)/2) + CELL/2; - - ui::Point thumbPos = c->NormaliseBlockCoord(ui::Point(thumbX, thumbY)); - - if(thumbPos.X<0) - thumbPos.X = 0; - if(thumbPos.X+(placeSaveThumb->Width)>=XRES) - thumbPos.X = XRES-placeSaveThumb->Width; - - if(thumbPos.Y<0) - thumbPos.Y = 0; - if(thumbPos.Y+(placeSaveThumb->Height)>=YRES) - thumbPos.Y = YRES-placeSaveThumb->Height; - - ren->draw_image(placeSaveThumb, thumbPos.X, thumbPos.Y, 128); - - ren->xor_rect(thumbPos.X, thumbPos.Y, placeSaveThumb->Width, placeSaveThumb->Height); + auto rect = RectSized(PlaceSavePos() * CELL, placeSaveThumb->Size()); + ren->BlendImage(placeSaveThumb->Data(), 0x80, rect); + ren->XorDottedRect(rect); } } else { if(selectPoint1.X==-1) { - ren->fillrect(0, 0, XRES, YRES, 0, 0, 0, 100); + ren->BlendFilledRect(RectSized(Vec2{ 0, 0 }, Vec2{ XRES, YRES }), 0x000000_rgb .WithAlpha(100)); } else { @@ -2179,19 +2186,21 @@ void GameView::OnDraw() if(y2>YRES-1) y2 = YRES-1; - ren->fillrect(0, 0, XRES, y1, 0, 0, 0, 100); - ren->fillrect(0, y2+1, XRES, YRES-y2-1, 0, 0, 0, 100); + ren->BlendFilledRect(RectSized(Vec2{ 0, 0 }, Vec2{ XRES, y1 }), 0x000000_rgb .WithAlpha(100)); + ren->BlendFilledRect(RectSized(Vec2{ 0, y2+1 }, Vec2{ XRES, YRES-y2-1 }), 0x000000_rgb .WithAlpha(100)); - ren->fillrect(0, y1, x1, (y2-y1)+1, 0, 0, 0, 100); - ren->fillrect(x2+1, y1, XRES-x2-1, (y2-y1)+1, 0, 0, 0, 100); + ren->BlendFilledRect(RectSized(Vec2{ 0, y1 }, Vec2{ x1, (y2-y1)+1 }), 0x000000_rgb .WithAlpha(100)); + ren->BlendFilledRect(RectSized(Vec2{ x2+1, y1 }, Vec2{ XRES-x2-1, (y2-y1)+1 }), 0x000000_rgb .WithAlpha(100)); - ren->xor_rect(x1, y1, (x2-x1)+1, (y2-y1)+1); + ren->XorDottedRect(RectBetween(Vec2{ x1, y1 }, Vec2{ x2, y2 })); } } } ren->RenderEnd(); + std::copy_n(ren->Data(), ren->Size().X * ren->Size().Y, g->Data()); + if (doScreenshot) { doScreenshot = false; @@ -2200,10 +2209,9 @@ void GameView::OnDraw() if(recording) { - VideoBuffer screenshot(ren->DumpFrame()); - std::vector data = format::VideoBufferToPPM(screenshot); + std::vector data = ren->DumpFrame().ToPPM(); - ByteString filename = ByteString::Build("recordings", PATH_SEP, recordingFolder, PATH_SEP, "frame_", Format::Width(recordingIndex++, 6), ".ppm"); + ByteString filename = ByteString::Build("recordings", PATH_SEP_CHAR, recordingFolder, PATH_SEP_CHAR, "frame_", Format::Width(recordingIndex++, 6), ".ppm"); Platform::WriteFile(data, filename); } @@ -2223,8 +2231,8 @@ void GameView::OnDraw() break; } startY -= 14; - g->fillrect(startX-3, startY-3, Graphics::textwidth(message)+6, 14, 0, 0, 0, std::min(100, alpha)); - g->drawtext(startX, startY, message, 255, 255, 255, alpha); + g->BlendFilledRect(RectSized(Vec2{ startX-3, startY-3 }, Vec2{ Graphics::TextSize(message).X + 5, 14 }), 0x000000_rgb .WithAlpha(std::min(100, alpha))); + g->BlendText({ startX, startY }, message, 0xFFFFFF_rgb .WithAlpha(alpha)); (*iter).second -= 3; } } @@ -2234,9 +2242,9 @@ void GameView::OnDraw() { String sampleInfo = String::Build("#", screenshotIndex, " ", String(0xE00E), " REC"); - int textWidth = Graphics::textwidth(sampleInfo); - g->fillrect(XRES-20-textWidth, 12, textWidth+8, 15, 0, 0, 0, 127); - g->drawtext(XRES-16-textWidth, 16, sampleInfo, 255, 50, 20, 255); + int textWidth = Graphics::TextSize(sampleInfo).X - 1; + g->BlendFilledRect(RectSized(Vec2{ XRES-20-textWidth, 12 }, Vec2{ textWidth+8, 15 }), 0x000000_rgb .WithAlpha(127)); + g->BlendText({ XRES-16-textWidth, 16 }, sampleInfo, 0xFF3214_rgb .WithAlpha(255)); } else if(showHud) { @@ -2355,15 +2363,15 @@ void GameView::OnDraw() sampleInfo << "Empty"; } - int textWidth = Graphics::textwidth(sampleInfo.Build()); - g->fillrect(XRES-20-textWidth, 12, textWidth+8, 15, 0, 0, 0, int(alpha*0.5f)); - g->drawtext(XRES-16-textWidth, 16, sampleInfo.Build(), 255, 255, 255, int(alpha*0.75f)); + int textWidth = Graphics::TextSize(sampleInfo.Build()).X - 1; + g->BlendFilledRect(RectSized(Vec2{ XRES-20-textWidth, 12 }, Vec2{ textWidth+8, 15 }), 0x000000_rgb .WithAlpha(int(alpha*0.5f))); + g->BlendText({ XRES-16-textWidth, 16 }, sampleInfo.Build(), 0xFFFFFF_rgb .WithAlpha(int(alpha*0.75f))); if (wavelengthGfx) { int i, cr, cg, cb, j, h = 3, x = XRES-19-textWidth, y = 10; int tmp; - g->fillrect(x, y, 30, h, 64, 64, 64, alpha); // coords -1 size +1 to work around bug in fillrect - TODO: fix fillrect + g->BlendFilledRect(RectSized(Vec2{ x, y }, Vec2{ 30, h }), 0x404040_rgb .WithAlpha(alpha)); for (i = 0; i < 30; i++) { if ((wavelengthGfx >> i)&1) @@ -2389,7 +2397,7 @@ void GameView::OnDraw() cg *= tmp; cb *= tmp; for (j=0; jblendpixel(x+29-i, y+j, cr>255?255:cr, cg>255?255:cg, cb>255?255:cb, alpha); + g->BlendPixel({ x+29-i, y+j }, RGBA(cr>255?255:cr, cg>255?255:cg, cb>255?255:cb, alpha)); } } } @@ -2413,9 +2421,9 @@ void GameView::OnDraw() format::RenderTemperature(sampleInfo, sample.AirTemperature, c->GetTemperatureScale()); } - textWidth = Graphics::textwidth(sampleInfo.Build()); - g->fillrect(XRES-20-textWidth, 27, textWidth+8, 14, 0, 0, 0, int(alpha*0.5f)); - g->drawtext(XRES-16-textWidth, 30, sampleInfo.Build(), 255, 255, 255, int(alpha*0.75f)); + auto textWidth = Graphics::TextSize(sampleInfo.Build()).X - 1; + g->BlendFilledRect(RectSized(Vec2{ XRES-20-textWidth, 27 }, Vec2{ textWidth+8, 14 }), 0x000000_rgb .WithAlpha(int(alpha*0.5f))); + g->BlendText({ XRES-16-textWidth, 30 }, sampleInfo.Build(), 0xFFFFFF_rgb .WithAlpha(int(alpha*0.75f))); } } @@ -2441,42 +2449,38 @@ void GameView::OnDraw() if (ren && ren->findingElement) fpsInfo << " [FIND]"; - int textWidth = Graphics::textwidth(fpsInfo.Build()); + int textWidth = Graphics::TextSize(fpsInfo.Build()).X - 1; int alpha = 255-introText*5; - g->fillrect(12, 12, textWidth+8, 15, 0, 0, 0, int(alpha*0.5)); - g->drawtext(16, 16, fpsInfo.Build(), 32, 216, 255, int(alpha*0.75)); + g->BlendFilledRect(RectSized(Vec2{ 12, 12 }, Vec2{ textWidth+8, 15 }), 0x000000_rgb .WithAlpha(int(alpha*0.5))); + g->BlendText({ 16, 16 }, fpsInfo.Build(), 0x20D8FF_rgb .WithAlpha(int(alpha*0.75))); } //Tooltips if(infoTipPresence) { int infoTipAlpha = (infoTipPresence>50?50:infoTipPresence)*5; - g->drawtext_outline((XRES-Graphics::textwidth(infoTip))/2, (YRES/2)-2, infoTip, 255, 255, 255, infoTipAlpha); + g->BlendTextOutline({ (XRES - (Graphics::TextSize(infoTip).X - 1)) / 2, YRES / 2 - 2 }, infoTip, 0xFFFFFF_rgb .WithAlpha(infoTipAlpha)); } if(toolTipPresence && toolTipPosition.X!=-1 && toolTipPosition.Y!=-1 && toolTip.length()) { if (toolTipPosition.Y == Size.Y-MENUSIZE-10) - g->drawtext_outline(toolTipPosition.X, toolTipPosition.Y, toolTip, 255, 255, 255, toolTipPresence>51?255:toolTipPresence*5); + g->BlendTextOutline(toolTipPosition, toolTip, 0xFFFFFF_rgb .WithAlpha(toolTipPresence>51?255:toolTipPresence*5)); else - g->drawtext(toolTipPosition.X, toolTipPosition.Y, toolTip, 255, 255, 255, toolTipPresence>51?255:toolTipPresence*5); + g->BlendText(toolTipPosition, toolTip, 0xFFFFFF_rgb .WithAlpha(toolTipPresence>51?255:toolTipPresence*5)); } if(buttonTipShow > 0) { - g->drawtext(16, Size.Y-MENUSIZE-24, buttonTip, 255, 255, 255, buttonTipShow>51?255:buttonTipShow*5); + g->BlendText({ 16, Size.Y-MENUSIZE-24 }, buttonTip, 0xFFFFFF_rgb .WithAlpha(buttonTipShow>51?255:buttonTipShow*5)); } //Introduction text if(introText && showHud) { - g->fillrect(0, 0, WINDOWW, WINDOWH, 0, 0, 0, introText>51?102:introText*2); - g->drawtext(16, 16, introTextMessage, 255, 255, 255, introText>51?255:introText*5); + g->BlendFilledRect(RectSized(Vec2{ 0, 0 }, WINDOW), 0x000000_rgb .WithAlpha(introText>51?102:introText*2)); + g->BlendText({ 16, 16 }, introTextMessage, 0xFFFFFF_rgb .WithAlpha(introText>51?255:introText*5)); } - - // Clear menu areas, to ensure particle graphics don't overlap - memset(g->vid+((XRES+BARSIZE)*YRES), 0, (PIXELSIZE*(XRES+BARSIZE))*MENUSIZE); - g->clearrect(XRES, 1, BARSIZE, YRES-1); } ui::Point GameView::lineSnapCoords(ui::Point point1, ui::Point point2) diff --git a/src/gui/game/GameView.h b/src/gui/game/GameView.h index 8d4092fb2..cfe6462ff 100644 --- a/src/gui/game/GameView.h +++ b/src/gui/game/GameView.h @@ -1,9 +1,8 @@ -#ifndef GAMEVIEW_H -#define GAMEVIEW_H - +#pragma once #include -#include #include +#include +#include #include "common/String.h" #include "gui/interface/Window.h" #include "simulation/Sample.h" @@ -80,7 +79,7 @@ private: ui::Point currentPoint, lastPoint; GameController * c; Renderer * ren; - Brush * activeBrush; + Brush const *activeBrush; //UI Elements std::vector quickOptionButtons; @@ -118,8 +117,12 @@ private: ui::Point currentMouse; ui::Point mousePosition; - VideoBuffer * placeSaveThumb; - ui::Point placeSaveOffset; + std::unique_ptr placeSaveThumb; + Mat2 placeSaveTransform = Mat2::Identity; + Vec2 placeSaveTranslate = Vec2::Zero; + void TranslateSave(Vec2 addToTranslate); + void TransformSave(Mat2 mulToTransform); + void ApplyTransformPlaceSave(); SimulationSample sample; @@ -135,6 +138,9 @@ private: void disableAltBehaviour(); void UpdateDrawMode(); void UpdateToolStrength(); + + Vec2 PlaceSavePos() const; + public: GameView(); virtual ~GameView(); @@ -156,8 +162,6 @@ public: bool AltBehaviour(){ return altBehaviour; } SelectMode GetSelectMode() { return selectMode; } void BeginStampSelection(); - ui::Point GetPlaceSaveOffset() { return placeSaveOffset; } - void SetPlaceSaveOffset(ui::Point offset) { placeSaveOffset = offset; } ByteString TakeScreenshot(int captureUI, int fileType); int Record(bool record); @@ -186,6 +190,7 @@ public: void NotifyColourPresetsChanged(GameModel * sender); void NotifyColourActivePresetChanged(GameModel * sender); void NotifyPlaceSaveChanged(GameModel * sender); + void NotifyTransformedPlaceSaveChanged(GameModel *sender); void NotifyNotificationsChanged(GameModel * sender); void NotifyLogChanged(GameModel * sender, String entry); void NotifyToolTipChanged(GameModel * sender); @@ -221,5 +226,3 @@ public: class OptionListener; }; - -#endif // GAMEVIEW_H diff --git a/src/gui/game/IntroText.h b/src/gui/game/IntroText.h index 2f89594d9..99813f85a 100644 --- a/src/gui/game/IntroText.h +++ b/src/gui/game/IntroText.h @@ -1,62 +1,70 @@ #pragma once #include "Config.h" +#include "common/String.h" -const char *const introTextData = - "\bl\bU" APPNAME "\bU - Version " MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) " - https://powdertoy.co.uk, irc.libera.chat #powder, https://tpt.io/discord\n" - "\n" - "\n" - "\bgControl+C/V/X are Copy, Paste and cut respectively.\n" - "\bgTo choose a material, hover over one of the icons on the right, it will show a selection of elements in that group.\n" - "\bgPick your material from the menu using mouse left/right buttons.\n" - "Draw freeform lines by dragging your mouse left/right button across the drawing area.\n" - "Shift+drag will create straight lines of particles.\n" - "Ctrl+drag will result in filled rectangles.\n" - "Ctrl+Shift+click will flood-fill a closed area.\n" - "Use the mouse scroll wheel, or '[' and ']', to change the tool size for particles.\n" - "Middle click or Alt+Click to \"sample\" the particles.\n" - "Ctrl+Z will act as Undo.\n" - "\n\boUse 'Z' for a zoom tool. Click to make the drawable zoom window stay around. Use the wheel to change the zoom strength.\n" - "The spacebar can be used to pause physics. Use 'F' to step ahead by one frame.\n" - "Use 'S' to save parts of the window as 'stamps'. 'L' loads the most recent stamp, 'K' shows a library of stamps you saved.\n" - "Use 'P' to take a screenshot and save it into the current directory.\n" - "Use 'H' to toggle the HUD. Use 'D' to toggle debug mode in the HUD.\n" - "\n" - "Contributors: \bgStanislaw K Skowronek (Designed the original Powder Toy),\n" - "\bgSimon Robertshaw, Skresanov Savely, cracker64, Catelite, Bryan Hoyle, Nathan Cousins, jacksonmj,\n" - "\bgFelix Wallin, Lieuwe Mosch, Anthony Boot, Me4502, MaksProg, jacob1, mniip, LBPHacker\n" - "\n" -#ifndef BETA - "\bgTo use online features such as saving, you need to register at: \brhttps://powdertoy.co.uk/Register.html\n" -#else - "\brThis is a BETA, you cannot save things publicly, nor open local saves and stamps made with it in older versions.\n" - "\brIf you are planning on publishing any saves, use the release version.\n" -#endif - "\n" - "\bt" MTOS(SAVE_VERSION) "." MTOS(MINOR_VERSION) "." MTOS(BUILD_NUM) " " IDENT -#ifdef SNAPSHOT - " SNAPSHOT " MTOS(SNAPSHOT_ID) -#elif MOD_ID > 0 - " MODVER " MTOS(SNAPSHOT_ID) -#endif -#if defined(X86_SSE) || defined(X86_SSE2) || defined(X86_SSE3) - " " IDENT_BUILD -#endif -#ifdef LUACONSOLE - " LUACONSOLE" -#endif -#ifdef GRAVFFT - " GRAVFFT" -#endif +inline ByteString IntroText() +{ + ByteStringBuilder sb; + sb << "\bl\bU" << APPNAME << "\bU - Version " << SAVE_VERSION << "." << MINOR_VERSION << " - https://powdertoy.co.uk, irc.libera.chat #powder, https://tpt.io/discord\n" + "\n" + "\n" + "\bgControl+C/V/X are Copy, Paste and cut respectively.\n" + "\bgTo choose a material, hover over one of the icons on the right, it will show a selection of elements in that group.\n" + "\bgPick your material from the menu using mouse left/right buttons.\n" + "Draw freeform lines by dragging your mouse left/right button across the drawing area.\n" + "Shift+drag will create straight lines of particles.\n" + "Ctrl+drag will result in filled rectangles.\n" + "Ctrl+Shift+click will flood-fill a closed area.\n" + "Use the mouse scroll wheel, or '[' and ']', to change the tool size for particles.\n" + "Middle click or Alt+Click to \"sample\" the particles.\n" + "Ctrl+Z will act as Undo.\n" + "\n\boUse 'Z' for a zoom tool. Click to make the drawable zoom window stay around. Use the wheel to change the zoom strength.\n" + "The spacebar can be used to pause physics. Use 'F' to step ahead by one frame.\n" + "Use 'S' to save parts of the window as 'stamps'. 'L' loads the most recent stamp, 'K' shows a library of stamps you saved.\n" + "Use 'P' to take a screenshot and save it into the current directory.\n" + "Use 'H' to toggle the HUD. Use 'D' to toggle debug mode in the HUD.\n" + "\n" + "Contributors: \bgStanislaw K Skowronek (Designed the original Powder Toy),\n" + "\bgSimon Robertshaw, Skresanov Savely, cracker64, Catelite, Victoria Hoyle, Nathan Cousins, jacksonmj,\n" + "\bgFelix Wallin, Lieuwe Mosch, Anthony Boot, Me4502, MaksProg, jacob1, mniip, LBPHacker\n" + "\n"; + if constexpr (BETA) + { + sb << "\brThis is a BETA, you cannot save things publicly, nor open local saves and stamps made with it in older versions.\n" + "\brIf you are planning on publishing any saves, use the release version.\n"; + } + else + { + sb << "\bgTo use online features such as saving, you need to register at: \brhttps://powdertoy.co.uk/Register.html\n"; + } + sb << "\n" + << "\bt" << SAVE_VERSION << "." << MINOR_VERSION << "." << BUILD_NUM << " " << IDENT; + if constexpr (SNAPSHOT) + { + sb << " SNAPSHOT " << SNAPSHOT_ID; + } + else if constexpr (MOD) + { + sb << " MODVER " << SNAPSHOT_ID; + } + if constexpr (LUACONSOLE) + { + sb << " LUACONSOLE"; + } #ifdef REALISTIC - " REALISTIC" + sb << " REALISTIC"; #endif -#ifdef NOHTTP - " NOHTTP" -#endif -#ifdef DEBUG - " DEBUG" -#endif -#ifdef ENFORCE_HTTPS - " HTTPS" -#endif - ; + if constexpr (NOHTTP) + { + sb << " NOHTTP"; + } + if constexpr (DEBUG) + { + sb << " DEBUG"; + } + if constexpr (ENFORCE_HTTPS) + { + sb << " HTTPS"; + } + return sb.Build(); +} diff --git a/src/gui/game/Menu.h b/src/gui/game/Menu.h index e52628306..df3130ab3 100644 --- a/src/gui/game/Menu.h +++ b/src/gui/game/Menu.h @@ -1,7 +1,4 @@ -#ifndef MENU_H_ -#define MENU_H_ -#include "Config.h" - +#pragma once #include "common/String.h" class Tool; @@ -39,5 +36,3 @@ public: void ClearTools(); }; - -#endif /* MENU_H_ */ diff --git a/src/gui/game/MenuButton.h b/src/gui/game/MenuButton.h index 1c0007d80..5eee431de 100644 --- a/src/gui/game/MenuButton.h +++ b/src/gui/game/MenuButton.h @@ -1,6 +1,4 @@ -#ifndef MENUBUTTON_H_ -#define MENUBUTTON_H_ - +#pragma once #include "gui/interface/Button.h" class MenuButton : public ui::Button @@ -10,5 +8,3 @@ public: int menuID; bool needsClick; }; - -#endif /* MENUBUTTON_H_ */ diff --git a/src/gui/game/Notification.h b/src/gui/game/Notification.h index dfa21d604..e45771896 100644 --- a/src/gui/game/Notification.h +++ b/src/gui/game/Notification.h @@ -1,6 +1,4 @@ -#ifndef NOTIFICATION_H_ -#define NOTIFICATION_H_ - +#pragma once #include "common/String.h" class Notification @@ -12,5 +10,3 @@ public: virtual void Action() { } }; - -#endif /* NOTIFICATION_H_ */ diff --git a/src/gui/game/PropertyTool.cpp b/src/gui/game/PropertyTool.cpp index 1e5decdf3..c873dc589 100644 --- a/src/gui/game/PropertyTool.cpp +++ b/src/gui/game/PropertyTool.cpp @@ -1,6 +1,6 @@ #include "Tool.h" -#include "client/Client.h" +#include "prefs/GlobalPrefs.h" #include "Menu.h" #include "Format.h" @@ -12,7 +12,6 @@ #include "gui/interface/Label.h" #include "gui/interface/Textbox.h" #include "gui/interface/DropDown.h" -#include "gui/interface/Keys.h" #include "gui/dialogues/ErrorMessage.h" #include "simulation/GOLString.h" @@ -22,7 +21,9 @@ #include "graphics/Graphics.h" +#include "Config.h" #include +#include class PropertyWindow: public ui::Window { @@ -75,12 +76,14 @@ sim(sim_) { property->AddOption(std::pair(properties[i].Name.FromAscii(), i)); } - property->SetOption(Client::Ref().GetPrefInteger("Prop.Type", 0)); + + auto &prefs = GlobalPrefs::Ref(); + property->SetOption(prefs.Get("Prop.Type", 0)); textField = new ui::Textbox(ui::Point(8, 46), ui::Point(Size.X-16, 16), "", "[value]"); textField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; textField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - textField->SetText(Client::Ref().GetPrefString("Prop.Value", "")); + textField->SetText(prefs.Get("Prop.Value", String(""))); AddComponent(textField); FocusComponent(textField); SetProperty(false); @@ -136,11 +139,11 @@ void PropertyWindow::SetProperty(bool warn) v = sim->GetParticleType(value.ToUtf8()); if (v == -1) { - for (auto *elementTool : tool->gameModel->GetMenuList()[SC_LIFE]->GetToolList()) + for (auto *elementTool : tool->gameModel.GetMenuList()[SC_LIFE]->GetToolList()) { - if (elementTool && elementTool->GetName() == value) + if (elementTool && elementTool->Name == value) { - v = ID(elementTool->GetToolID()); + v = ID(elementTool->ToolID); break; } } @@ -162,11 +165,10 @@ void PropertyWindow::SetProperty(bool warn) new ErrorMessage("Could not set property", "Invalid particle type"); return; } - -#ifdef DEBUG - std::cout << "Got int value " << v << std::endl; -#endif - + if constexpr (DEBUG) + { + std::cout << "Got int value " << v << std::endl; + } tool->propValue.Integer = v; break; } @@ -187,16 +189,17 @@ void PropertyWindow::SetProperty(bool warn) { v = value.ToNumber(); } -#ifdef DEBUG - std::cout << "Got uint value " << v << std::endl; -#endif + if constexpr (DEBUG) + { + std::cout << "Got uint value " << v << std::endl; + } tool->propValue.UInteger = v; break; } case StructProperty::Float: { if (properties[property->GetOption().second].Name == "temp") - tool->propValue.Float = format::StringToTemperature(value, tool->gameModel->GetTemperatureScale()); + tool->propValue.Float = format::StringToTemperature(value, tool->gameModel.GetTemperatureScale()); else tool->propValue.Float = value.ToNumber(); } @@ -216,8 +219,12 @@ void PropertyWindow::SetProperty(bool warn) new ErrorMessage("Could not set property", "Invalid value provided"); return; } - Client::Ref().SetPref("Prop.Type", property->GetOption().second); - Client::Ref().SetPrefUnicode("Prop.Value", textField->GetText()); + { + auto &prefs = GlobalPrefs::Ref(); + Prefs::DeferWrite dw(prefs); + prefs.Set("Prop.Type", property->GetOption().second); + prefs.Set("Prop.Value", textField->GetText()); + } } } @@ -231,8 +238,8 @@ void PropertyWindow::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); } void PropertyWindow::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) @@ -281,24 +288,21 @@ void PropertyTool::SetProperty(Simulation *sim, ui::Point position) } } -void PropertyTool::Draw(Simulation *sim, Brush *cBrush, ui::Point position) +void PropertyTool::Draw(Simulation *sim, Brush const &cBrush, ui::Point position) { - if(cBrush) + for (ui::Point off : cBrush) { - int radiusX = cBrush->GetRadius().X, radiusY = cBrush->GetRadius().Y, sizeX = cBrush->GetSize().X, sizeY = cBrush->GetSize().Y; - unsigned char *bitmap = cBrush->GetBitmap(); - for(int y = 0; y < sizeY; y++) - for(int x = 0; x < sizeX; x++) - if(bitmap[(y*sizeX)+x] && (position.X+(x-radiusX) >= 0 && position.Y+(y-radiusY) >= 0 && position.X+(x-radiusX) < XRES && position.Y+(y-radiusY) < YRES)) - SetProperty(sim, ui::Point(position.X+(x-radiusX), position.Y+(y-radiusY))); + ui::Point coords = position + off; + if (coords.X >= 0 && coords.Y >= 0 && coords.X < XRES && coords.Y < YRES) + SetProperty(sim, coords); } } -void PropertyTool::DrawLine(Simulation *sim, Brush *cBrush, ui::Point position, ui::Point position2, bool dragging) +void PropertyTool::DrawLine(Simulation *sim, Brush const &cBrush, ui::Point position, ui::Point position2, bool dragging) { int x1 = position.X, y1 = position.Y, x2 = position2.X, y2 = position2.Y; bool reverseXY = abs(y2-y1) > abs(x2-x1); - int x, y, dx, dy, sy, rx = cBrush->GetRadius().X, ry = cBrush->GetRadius().Y; + int x, y, dx, dy, sy, rx = cBrush.GetRadius().X, ry = cBrush.GetRadius().Y; float e = 0.0f, de; if (reverseXY) { @@ -348,7 +352,7 @@ void PropertyTool::DrawLine(Simulation *sim, Brush *cBrush, ui::Point position, } } -void PropertyTool::DrawRect(Simulation *sim, Brush *cBrush, ui::Point position, ui::Point position2) +void PropertyTool::DrawRect(Simulation *sim, Brush const &cBrush, ui::Point position, ui::Point position2) { int x1 = position.X, y1 = position.Y, x2 = position2.X, y2 = position2.Y; int i, j; @@ -369,7 +373,7 @@ void PropertyTool::DrawRect(Simulation *sim, Brush *cBrush, ui::Point position, SetProperty(sim, ui::Point(i, j)); } -void PropertyTool::DrawFill(Simulation *sim, Brush *cBrush, ui::Point position) +void PropertyTool::DrawFill(Simulation *sim, Brush const &cBrush, ui::Point position) { if (validProperty) sim->flood_prop(position.X, position.Y, propOffset, propValue, propType); diff --git a/src/gui/game/QuickOption.h b/src/gui/game/QuickOption.h index 67e1f011e..392bf82cb 100644 --- a/src/gui/game/QuickOption.h +++ b/src/gui/game/QuickOption.h @@ -1,8 +1,5 @@ #pragma once -#include "Config.h" - #include "common/String.h" - #include class GameModel; diff --git a/src/gui/game/RectangleBrush.h b/src/gui/game/RectangleBrush.h new file mode 100644 index 000000000..3695c2f3a --- /dev/null +++ b/src/gui/game/RectangleBrush.h @@ -0,0 +1,21 @@ +#pragma once +#include "Brush.h" + +class RectangleBrush: public Brush +{ +public: + virtual ~RectangleBrush() override = default; + + std::unique_ptr GenerateBitmap() const override + { + auto size = GetSize(); + auto bitmap = std::make_unique(size.X * size.Y); + std::fill(&bitmap[0], &bitmap[0] + size.X * size.Y, 0xFF); + return bitmap; + } + + std::unique_ptr Clone() const override + { + return std::make_unique(*this); + } +}; diff --git a/src/gui/game/RenderPreset.h b/src/gui/game/RenderPreset.h index 301d16ea5..2e65fcab4 100644 --- a/src/gui/game/RenderPreset.h +++ b/src/gui/game/RenderPreset.h @@ -1,5 +1,7 @@ -#ifndef RENDER_PRESET_H -#define RENDER_PRESET_H +#pragma once +#include "common/String.h" +#include + class RenderPreset { public: @@ -16,4 +18,3 @@ public: ColourMode(colourMode) {} }; -#endif diff --git a/src/gui/game/SampleTool.cpp b/src/gui/game/SampleTool.cpp index bacf49d57..294e0ea50 100644 --- a/src/gui/game/SampleTool.cpp +++ b/src/gui/game/SampleTool.cpp @@ -11,30 +11,21 @@ #include "Menu.h" -VideoBuffer * SampleTool::GetIcon(int toolID, int width, int height) +std::unique_ptr SampleTool::GetIcon(int toolID, Vec2 size) { - VideoBuffer * newTexture = new VideoBuffer(width, height); - for (int y=0; ySetPixel(x, y, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - } - newTexture->AddCharacter((width/2)-5, (height/2)-5, 0xE066, 255, 255, 255, 255); - newTexture->BlendPixel(10, 9, 100, 180, 255, 255); - newTexture->BlendPixel(11, 8, 100, 180, 255, 255); - newTexture->BlendPixel(12, 7, 100, 180, 255, 255); - return newTexture; + auto texture = std::make_unique(size); + texture->DrawRect(size.OriginRect(), 0xA0A0A0_rgb); + texture->BlendChar((size / 2) - Vec2(5, 5), 0xE066, 0xFFFFFF_rgb .WithAlpha(0xFF)); + texture->BlendChar((size / 2) - Vec2(5, 5), 0xE06B, 0x64B4FF_rgb .WithAlpha(0xFF)); + return texture; } -void SampleTool::Draw(Simulation * sim, Brush * brush, ui::Point position) +void SampleTool::Draw(Simulation * sim, Brush const &brush, ui::Point position) { - if(gameModel->GetColourSelectorVisibility()) + if(gameModel.GetColourSelectorVisibility()) { - pixel colour = gameModel->GetRenderer()->sampleColor; - gameModel->SetColourSelectorColour(ui::Colour(PIXR(colour), PIXG(colour), PIXB(colour), 255)); + pixel colour = gameModel.GetRenderer()->sampleColor; + gameModel.SetColourSelectorColour(RGB::Unpack(colour).WithAlpha(0xFF)); } else { @@ -52,25 +43,24 @@ void SampleTool::Draw(Simulation * sim, Brush * brush, ui::Point position) if (part->type == PT_LIFE) { bool found = false; - for (auto *elementTool : gameModel->GetMenuList()[SC_LIFE]->GetToolList()) + for (auto *elementTool : gameModel.GetMenuList()[SC_LIFE]->GetToolList()) { - if (elementTool && ID(elementTool->GetToolID()) == part->ctype) + if (elementTool && ID(elementTool->ToolID) == part->ctype) { - gameModel->SetActiveTool(0, elementTool); + gameModel.SetActiveTool(0, elementTool); found = true; break; } } if (!found) { - ((GOLTool *)(gameModel->GetToolFromIdentifier("DEFAULT_UI_ADDLIFE")))->OpenWindow(gameModel->GetSimulation(), 0, part->ctype, part->dcolour, part->tmp); + static_cast(gameModel.GetToolFromIdentifier("DEFAULT_UI_ADDLIFE"))->OpenWindow(gameModel.GetSimulation(), 0, part->ctype, RGB::Unpack(part->dcolour & 0xFFFFFF), RGB::Unpack(part->tmp & 0xFFFFFF)); } } else { - Tool * elementTool = gameModel->GetElementTool(part->type); - if(elementTool) - gameModel->SetActiveTool(0, elementTool); + if (auto elementTool = gameModel.GetElementTool(part->type)) + gameModel.SetActiveTool(0, elementTool); } } } diff --git a/src/gui/game/SignTool.cpp b/src/gui/game/SignTool.cpp index 9f2142c0e..88969f017 100644 --- a/src/gui/game/SignTool.cpp +++ b/src/gui/game/SignTool.cpp @@ -168,9 +168,9 @@ void SignWindow::DoDraw() Graphics * g = GetGraphics(); String text = currentSign.getDisplayText(sim, x, y, w, h); - g->clearrect(x, y, w+1, h); - g->drawrect(x, y, w+1, h, 192, 192, 192, 255); - g->drawtext(x+3, y+4, text, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Vec2{ x + 1, y + 1 }, Vec2{ w, h - 1 }), 0x000000_rgb); + g->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w+1, h }), 0xC0C0C0_rgb); + g->BlendText({ x+3, y+4 }, text, 0xFFFFFF_rgb .WithAlpha(255)); if (currentSign.ju != sign::None) { @@ -180,7 +180,7 @@ void SignWindow::DoDraw() dy = (currentSign.y > 18) ? -1 : 1; for (int j=0; j<4; j++) { - g->blendpixel(x, y, 192, 192, 192, 255); + g->DrawPixel({ x, y }, 0xC0C0C0_rgb); x+=dx; y+=dy; } @@ -197,7 +197,7 @@ void SignWindow::DoMouseMove(int x, int y, int dx, int dy) { ui::Window::DoMouseMove(x, y, dx, dy); else { - ui::Point pos = tool->gameModel->AdjustZoomCoords(ui::Point(x, y)); + ui::Point pos = tool->gameModel.AdjustZoomCoords(ui::Point(x, y)); if(pos.X < XRES && pos.Y < YRES) { movingSign->x = pos.X; @@ -222,27 +222,20 @@ void SignWindow::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); } -VideoBuffer * SignTool::GetIcon(int toolID, int width, int height) +std::unique_ptr SignTool::GetIcon(int toolID, Vec2 size) { - VideoBuffer * newTexture = new VideoBuffer(width, height); - for (int y=0; ySetPixel(x, y, PIXR(pc), PIXG(pc), PIXB(pc), 255); - } - } - newTexture->AddCharacter((width/2)-5, (height/2)-5, 0xE021, 32, 64, 128, 255); - newTexture->BlendCharacter((width/2)-5, (height/2)-5, 0xE020, 255, 255, 255, 255); - return newTexture; + auto texture = std::make_unique(size); + texture->DrawRect(size.OriginRect(), 0xA0A0A0_rgb); + texture->BlendChar((size / 2) - Vec2(5, 5), 0xE021, 0x204080_rgb .WithAlpha(0xFF)); + texture->BlendChar((size / 2) - Vec2(5, 5), 0xE020, 0xFFFFFF_rgb .WithAlpha(0xFF)); + return texture; } -void SignTool::Click(Simulation * sim, Brush * brush, ui::Point position) +void SignTool::Click(Simulation * sim, Brush const &brush, ui::Point position) { int signX, signY, signW, signH, signIndex = -1; for (size_t i = 0; i < sim->signs.size(); i++) diff --git a/src/gui/game/Tool.cpp b/src/gui/game/Tool.cpp index 59263519f..160cb8595 100644 --- a/src/gui/game/Tool.cpp +++ b/src/gui/game/Tool.cpp @@ -1,95 +1,59 @@ #include "Tool.h" +#include "graphics/Graphics.h" #include "gui/game/Brush.h" #include "simulation/Simulation.h" #include "simulation/SimulationData.h" #include "simulation/ElementClasses.h" -Tool::Tool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int)): - textureGen(textureGen), - toolID(id), - toolName(name), - toolDescription(description), - strength(1.0f), - blocky(false), - identifier(identifier), - colRed(r), - colGreen(g), - colBlue(b) +std::unique_ptr Tool::GetTexture(Vec2 size) { + return textureGen ? textureGen(ToolID, size) : nullptr; } -VideoBuffer * Tool::GetTexture(int width, int height) -{ - if(textureGen) - { - return textureGen(toolID, width, height); - } - return NULL; +void Tool::Click(Simulation * sim, Brush const &brush, ui::Point position) { } +void Tool::Draw(Simulation * sim, Brush const &brush, ui::Point position) { + sim->ToolBrush(position.X, position.Y, ToolID, brush, Strength); } -void Tool::SetTextureGen(VideoBuffer * (*textureGen)(int, int, int)) -{ - this->textureGen = textureGen; +void Tool::DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging) { + sim->ToolLine(position1.X, position1.Y, position2.X, position2.Y, ToolID, brush, Strength); } -ByteString Tool::GetIdentifier() { return identifier; } -String Tool::GetName() { return toolName; } -String Tool::GetDescription() { return toolDescription; } -Tool::~Tool() {} - -void Tool::Click(Simulation * sim, Brush * brush, ui::Point position) { } -void Tool::Draw(Simulation * sim, Brush * brush, ui::Point position) { - sim->ToolBrush(position.X, position.Y, toolID, brush, strength); +void Tool::DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) { + sim->ToolBox(position1.X, position1.Y, position2.X, position2.Y, ToolID, Strength); } -void Tool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { - sim->ToolLine(position1.X, position1.Y, position2.X, position2.Y, toolID, brush, strength); -} -void Tool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { - sim->ToolBox(position1.X, position1.Y, position2.X, position2.Y, toolID, strength); -} -void Tool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) {} +void Tool::DrawFill(Simulation * sim, Brush const &brush, ui::Point position) {} -ElementTool::ElementTool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int)): - Tool(id, name, description, r, g, b, identifier, textureGen) -{ +void ElementTool::Draw(Simulation * sim, Brush const &brush, ui::Point position){ + sim->CreateParts(position.X, position.Y, ToolID, brush); } -ElementTool::~ElementTool() {} -void ElementTool::Draw(Simulation * sim, Brush * brush, ui::Point position){ - sim->CreateParts(position.X, position.Y, toolID, brush); +void ElementTool::DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging) { + sim->CreateLine(position1.X, position1.Y, position2.X, position2.Y, ToolID, brush); } -void ElementTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { - sim->CreateLine(position1.X, position1.Y, position2.X, position2.Y, toolID, brush); +void ElementTool::DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) { + sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, ToolID); } -void ElementTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { - sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, toolID); -} -void ElementTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) { - sim->FloodParts(position.X, position.Y, toolID, -1); +void ElementTool::DrawFill(Simulation * sim, Brush const &brush, ui::Point position) { + sim->FloodParts(position.X, position.Y, ToolID, -1); } -WallTool::WallTool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int)): -Tool(id, name, description, r, g, b, identifier, textureGen) -{ - blocky = true; +void WallTool::Draw(Simulation * sim, Brush const &brush, ui::Point position) { + sim->CreateWalls(position.X, position.Y, 1, 1, ToolID, &brush); } -WallTool::~WallTool() {} -void WallTool::Draw(Simulation * sim, Brush * brush, ui::Point position) { - sim->CreateWalls(position.X, position.Y, 1, 1, toolID, brush); -} -void WallTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) { +void WallTool::DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging) { int wallX = position1.X/CELL; int wallY = position1.Y/CELL; - if(dragging == false && toolID == WL_FAN && sim->bmap[wallY][wallX]==WL_FAN) + if(dragging == false && ToolID == WL_FAN && sim->bmap[wallY][wallX]==WL_FAN) { float newFanVelX = (position2.X-position1.X)*0.005f; - newFanVelX *= strength; + newFanVelX *= Strength; float newFanVelY = (position2.Y-position1.Y)*0.005f; - newFanVelY *= strength; + newFanVelY *= Strength; sim->FloodWalls(position1.X, position1.Y, WL_FLOODHELPER, WL_FAN); - for (int j = 0; j < YRES/CELL; j++) - for (int i = 0; i < XRES/CELL; i++) + for (int j = 0; j < YCELLS; j++) + for (int i = 0; i < XCELLS; i++) if (sim->bmap[j][i] == WL_FLOODHELPER) { sim->fvx[j][i] = newFanVelX; @@ -99,69 +63,52 @@ void WallTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui } else { - sim->CreateWallLine(position1.X, position1.Y, position2.X, position2.Y, 1, 1, toolID, brush); + sim->CreateWallLine(position1.X, position1.Y, position2.X, position2.Y, 1, 1, ToolID, &brush); } } -void WallTool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { - sim->CreateWallBox(position1.X, position1.Y, position2.X, position2.Y, toolID); +void WallTool::DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) { + sim->CreateWallBox(position1.X, position1.Y, position2.X, position2.Y, ToolID); } -void WallTool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) { - if (toolID != WL_STREAM) - sim->FloodWalls(position.X, position.Y, toolID, -1); +void WallTool::DrawFill(Simulation * sim, Brush const &brush, ui::Point position) { + if (ToolID != WL_STREAM) + sim->FloodWalls(position.X, position.Y, ToolID, -1); } -WindTool::WindTool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int)): - Tool(id, name, description, r, g, b, identifier, textureGen) +void WindTool::DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging) { -} - -void WindTool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) -{ - int radiusX, radiusY, sizeX, sizeY; - float strength = dragging?0.01f:0.002f; - strength *= this->strength; + strength *= this->Strength; - radiusX = brush->GetRadius().X; - radiusY = brush->GetRadius().Y; - - sizeX = brush->GetSize().X; - sizeY = brush->GetSize().Y; - - unsigned char *bitmap = brush->GetBitmap(); - - for(int y = 0; y < sizeY; y++) + for (ui::Point off : brush) { - for(int x = 0; x < sizeX; x++) + ui::Point coords = position1 + off; + if (coords.X >= 0 && coords.Y >= 0 && coords.X < XRES && coords.Y < YRES) { - if(bitmap[(y*sizeX)+x] && (position1.X+(x-radiusX) >= 0 && position1.Y+(y-radiusY) >= 0 && position1.X+(x-radiusX) < XRES && position1.Y+(y-radiusY) < YRES)) - { - sim->vx[(position1.Y+(y-radiusY))/CELL][(position1.X+(x-radiusX))/CELL] += (position2.X-position1.X)*strength; - sim->vy[(position1.Y+(y-radiusY))/CELL][(position1.X+(x-radiusX))/CELL] += (position2.Y-position1.Y)*strength; - } + sim->vx[coords.Y / CELL][coords.X / CELL] += (position2 - position1).X * strength; + sim->vy[coords.Y / CELL][coords.X / CELL] += (position2 - position1).Y * strength; } } } -void Element_LIGH_Tool::DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging) +void Element_LIGH_Tool::DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging) { if (dragging) - sim->CreateParts(position1.X, position1.Y, brush->GetRadius().X, brush->GetRadius().Y, PT_LIGH); + sim->CreateParts(position1.X, position1.Y, brush.GetRadius().X, brush.GetRadius().Y, PT_LIGH); } -void Element_TESC_Tool::DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) { - int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7; - sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, toolID | PMAPID(radiusInfo)); +void Element_TESC_Tool::DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) { + int radiusInfo = brush.GetRadius().X*4+brush.GetRadius().Y*4+7; + sim->CreateBox(position1.X, position1.Y, position2.X, position2.Y, ToolID | PMAPID(radiusInfo)); } -void Element_TESC_Tool::DrawFill(Simulation * sim, Brush * brush, ui::Point position) { - int radiusInfo = brush->GetRadius().X*4+brush->GetRadius().Y*4+7; - sim->FloodParts(position.X, position.Y, toolID | PMAPID(radiusInfo), -1); +void Element_TESC_Tool::DrawFill(Simulation * sim, Brush const &brush, ui::Point position) { + int radiusInfo = brush.GetRadius().X*4+brush.GetRadius().Y*4+7; + sim->FloodParts(position.X, position.Y, ToolID | PMAPID(radiusInfo), -1); } -void PlopTool::Click(Simulation * sim, Brush * brush, ui::Point position) +void PlopTool::Click(Simulation * sim, Brush const &brush, ui::Point position) { - sim->create_part(-2, position.X, position.Y, TYP(toolID), ID(toolID)); + sim->create_part(-2, position.X, position.Y, TYP(ToolID), ID(ToolID)); } diff --git a/src/gui/game/Tool.h b/src/gui/game/Tool.h index 0464e44a0..d86c69f3b 100644 --- a/src/gui/game/Tool.h +++ b/src/gui/game/Tool.h @@ -1,8 +1,8 @@ -#ifndef TOOL_H_ -#define TOOL_H_ -#include "Config.h" - +#pragma once +#include #include "common/String.h" +#include "common/Vec2.h" +#include "graphics/Pixel.h" #include "gui/interface/Point.h" #include "simulation/StructProperty.h" @@ -12,186 +12,241 @@ class VideoBuffer; class Tool { -protected: - VideoBuffer * (*textureGen)(int, int, int); - int toolID; - String toolName; - String toolDescription; - float strength; - bool blocky; - ByteString identifier; -public: - int colRed, colGreen, colBlue; +private: + std::unique_ptr (*const textureGen)(int, Vec2); - Tool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); - int GetToolID() { return toolID; } - String GetName(); - String GetDescription(); - ByteString GetIdentifier(); - int GetBlocky() { return blocky; } - void SetStrength(float value) { strength = value; } - float GetStrength() { return strength; } - VideoBuffer * GetTexture(int width, int height); - void SetTextureGen(VideoBuffer * (*textureGen)(int, int, int)); - virtual ~Tool(); - virtual void Click(Simulation * sim, Brush * brush, ui::Point position); - virtual void Draw(Simulation * sim, Brush * brush, ui::Point position); - virtual void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false); - virtual void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2); - virtual void DrawFill(Simulation * sim, Brush * brush, ui::Point position); +public: + int const ToolID; + String const Name; + String const Description; + ByteString const Identifier; + RGB const Colour; + bool const Blocky; + float Strength = 1.0f; + + Tool(int id, String name, String description, + RGB colour, ByteString identifier, std::unique_ptr (*textureGen)(int, Vec2) = NULL, bool blocky = false + ): + textureGen(textureGen), + ToolID(id), + Name(name), + Description(description), + Identifier(identifier), + Colour(colour), + Blocky(blocky) + {} + + virtual ~Tool() + {} + + std::unique_ptr GetTexture(Vec2); + virtual void Click(Simulation * sim, Brush const &brush, ui::Point position); + virtual void Draw(Simulation * sim, Brush const &brush, ui::Point position); + virtual void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false); + virtual void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2); + virtual void DrawFill(Simulation * sim, Brush const &brush, ui::Point position); }; class GameModel; class SignTool: public Tool { + GameModel &gameModel; + + friend class SignWindow; + public: - GameModel * gameModel; - SignTool(GameModel *model): - Tool(0, "SIGN", "Sign. Displays text. Click on a sign to edit it or anywhere else to place a new one.", 0, 0, 0, "DEFAULT_UI_SIGN", SignTool::GetIcon), - gameModel(model) - { - } - static VideoBuffer * GetIcon(int toolID, int width, int height); - virtual ~SignTool() {} - void Click(Simulation * sim, Brush * brush, ui::Point position) override; - void Draw(Simulation * sim, Brush * brush, ui::Point position) override { } - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override { } - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override { } - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override { } + SignTool(GameModel &model): + Tool(0, "SIGN", "Sign. Displays text. Click on a sign to edit it or anywhere else to place a new one.", + 0x000000_rgb, "DEFAULT_UI_SIGN", SignTool::GetIcon + ), + gameModel(model) + {} + + virtual ~SignTool() + {} + + static std::unique_ptr GetIcon(int toolID, Vec2 size); + void Click(Simulation * sim, Brush const &brush, ui::Point position) override; + void Draw(Simulation * sim, Brush const &brush, ui::Point position) override { } + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override { } + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override { } + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override { } }; class SampleTool: public Tool { - GameModel * gameModel; + GameModel &gameModel; + public: - SampleTool(GameModel *model): - Tool(0, "SMPL", "Sample an element on the screen.", 0, 0, 0, "DEFAULT_UI_SAMPLE", SampleTool::GetIcon), - gameModel(model) - { - } - static VideoBuffer * GetIcon(int toolID, int width, int height); + SampleTool(GameModel &model): + Tool(0, "SMPL", "Sample an element on the screen.", + 0x000000_rgb, "DEFAULT_UI_SAMPLE", SampleTool::GetIcon + ), + gameModel(model) + {} + + static std::unique_ptr GetIcon(int toolID, Vec2 size); virtual ~SampleTool() {} - void Click(Simulation * sim, Brush * brush, ui::Point position) override { } - void Draw(Simulation * sim, Brush * brush, ui::Point position) override; - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override { } - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override { } - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override { } + void Click(Simulation * sim, Brush const &brush, ui::Point position) override { } + void Draw(Simulation * sim, Brush const &brush, ui::Point position) override; + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override { } + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override { } + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override { } }; class PropertyTool: public Tool { -public: - GameModel * gameModel; - PropertyTool(GameModel *model): - Tool(0, "PROP", "Property Drawing Tool. Use to alter the properties of elements in the field.", 0xfe, 0xa9, 0x00, "DEFAULT_UI_PROPERTY", NULL), - gameModel(model) - { - } + GameModel &gameModel; StructProperty::PropertyType propType; PropertyValue propValue; bool changeType; size_t propOffset; bool validProperty; + friend class PropertyWindow; + +public: + PropertyTool(GameModel &model): + Tool(0, "PROP", "Property Drawing Tool. Use to alter the properties of elements in the field.", + 0xFEA900_rgb, "DEFAULT_UI_PROPERTY", NULL + ), + gameModel(model) + {} + + virtual ~PropertyTool() + {} + void OpenWindow(Simulation *sim); - virtual ~PropertyTool() {} virtual void SetProperty(Simulation *sim, ui::Point position); - void Click(Simulation * sim, Brush * brush, ui::Point position) override { } - void Draw(Simulation *sim, Brush *brush, ui::Point position) override; - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override; - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override; - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override; + void Click(Simulation * sim, Brush const &brush, ui::Point position) override { } + void Draw(Simulation *sim, Brush const &brush, ui::Point position) override; + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override; + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override; + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override; }; class GOLTool: public Tool { + GameModel &gameModel; public: - GameModel * gameModel; - GOLTool(GameModel * gameModel): - Tool(0, "CUST", "Add a new custom GOL type. (Use ctrl+shift+rightclick to remove them)", 0xfe, 0xa9, 0x00, "DEFAULT_UI_ADDLIFE", NULL), - gameModel(gameModel) - { - } - void OpenWindow(Simulation *sim, int toolSelection, int rule = 0, int colour1 = 0, int colour2 = 0); - virtual ~GOLTool() {} - void Click(Simulation * sim, Brush * brush, ui::Point position) override { } - void Draw(Simulation *sim, Brush *brush, ui::Point position) override { }; - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override { }; - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override { }; - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override { }; + GOLTool(GameModel &gameModel): + Tool(0, "CUST", "Add a new custom GOL type. (Use ctrl+shift+rightclick to remove them)", + 0xFEA900_rgb, "DEFAULT_UI_ADDLIFE", NULL + ), + gameModel(gameModel) + {} + + virtual ~GOLTool() + {} + + void OpenWindow(Simulation *sim, int toolSelection, int rule = 0, RGB colour1 = 0x000000_rgb, RGB colour2 = 0x000000_rgb); + void Click(Simulation * sim, Brush const &brush, ui::Point position) override { } + void Draw(Simulation *sim, Brush const &brush, ui::Point position) override { }; + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override { }; + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override { }; + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override { }; }; class ElementTool: public Tool { public: - ElementTool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); - virtual ~ElementTool(); - void Draw(Simulation * sim, Brush * brush, ui::Point position) override; - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override; - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override; - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override; + ElementTool(int id, String name, String description, + RGB colour, ByteString identifier, std::unique_ptr (*textureGen)(int, Vec2) = NULL): + Tool(id, name, description, colour, identifier, textureGen) + {} + + virtual ~ElementTool() + {} + + void Draw(Simulation * sim, Brush const &brush, ui::Point position) override; + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override; + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override; + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override; }; class Element_LIGH_Tool: public ElementTool { public: - Element_LIGH_Tool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL): - ElementTool(id, name, description, r, g, b, identifier, textureGen) - { } - virtual ~Element_LIGH_Tool() { } - void Click(Simulation * sim, Brush * brush, ui::Point position) override { } - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override; - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override { } - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override { } + Element_LIGH_Tool(int id, String name, String description, + RGB colour, ByteString identifier, std::unique_ptr (*textureGen)(int, Vec2) = NULL): + ElementTool(id, name, description, colour, identifier, textureGen) + {} + + virtual ~Element_LIGH_Tool() + {} + + void Click(Simulation * sim, Brush const &brush, ui::Point position) override { } + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override; + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override { } + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override { } }; class Element_TESC_Tool: public ElementTool { public: - Element_TESC_Tool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL): - ElementTool(id, name, description, r, g, b, identifier, textureGen) - { } - virtual ~Element_TESC_Tool() {} - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override; - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override; + Element_TESC_Tool(int id, String name, String description, + RGB colour, ByteString identifier, std::unique_ptr (*textureGen)(int, Vec2) = NULL): + ElementTool(id, name, description, colour, identifier, textureGen) + {} + + virtual ~Element_TESC_Tool() + {} + + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override; + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override; }; class PlopTool: public ElementTool { public: - PlopTool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL): - ElementTool(id, name, description, r, g, b, identifier, textureGen) - { } - virtual ~PlopTool() { } - void Draw(Simulation * sim, Brush * brush, ui::Point position) override { } - void Click(Simulation * sim, Brush * brush, ui::Point position) override; - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override { } - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override { } - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override { } + PlopTool(int id, String name, String description, + RGB colour, ByteString identifier, std::unique_ptr (*textureGen)(int, Vec2) = NULL): + ElementTool(id, name, description, colour, identifier, textureGen) + {} + + virtual ~PlopTool() + {} + + void Draw(Simulation * sim, Brush const &brush, ui::Point position) override { } + void Click(Simulation * sim, Brush const &brush, ui::Point position) override; + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override { } + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override { } + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override { } }; class WallTool: public Tool { public: - WallTool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); - virtual ~WallTool(); - void Draw(Simulation * sim, Brush * brush, ui::Point position) override; - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override; - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override; - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override; + WallTool(int id, String description, + RGB colour, ByteString identifier, std::unique_ptr (*textureGen)(int, Vec2) = NULL): + Tool(id, "", description, colour, identifier, textureGen, true) + { + } + + virtual ~WallTool() + {} + + void Draw(Simulation * sim, Brush const &brush, ui::Point position) override; + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override; + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override; + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override; }; class WindTool: public Tool { public: - WindTool(int id, String name, String description, int r, int g, int b, ByteString identifier, VideoBuffer * (*textureGen)(int, int, int) = NULL); - virtual ~WindTool() { } - void Draw(Simulation * sim, Brush * brush, ui::Point position) override { } - void DrawLine(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2, bool dragging = false) override; - void DrawRect(Simulation * sim, Brush * brush, ui::Point position1, ui::Point position2) override { } - void DrawFill(Simulation * sim, Brush * brush, ui::Point position) override { } -}; + WindTool(): + Tool(0, "WIND", "Creates air movement.", + 0x404040_rgb, "DEFAULT_UI_WIND") + {} -#endif /* TOOL_H_ */ + virtual ~WindTool() + {} + + void Draw(Simulation * sim, Brush const &brush, ui::Point position) override { } + void DrawLine(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2, bool dragging = false) override; + void DrawRect(Simulation * sim, Brush const &brush, ui::Point position1, ui::Point position2) override { } + void DrawFill(Simulation * sim, Brush const &brush, ui::Point position) override { } +}; diff --git a/src/gui/game/ToolButton.cpp b/src/gui/game/ToolButton.cpp index 3432ad212..5fafc498a 100644 --- a/src/gui/game/ToolButton.cpp +++ b/src/gui/game/ToolButton.cpp @@ -1,11 +1,7 @@ #include "ToolButton.h" - #include "graphics/Graphics.h" - -#include "gui/interface/Keys.h" -#include "gui/interface/Mouse.h" - #include "Favorite.h" +#include ToolButton::ToolButton(ui::Point position, ui::Point size, String text, ByteString toolIdentifier, String toolTip): ui::Button(position, size, text, toolTip), @@ -49,50 +45,46 @@ void ToolButton::OnMouseUp(int x, int y, unsigned int button) void ToolButton::Draw(const ui::Point& screenPos) { Graphics * g = GetGraphics(); - int x = clipRectX; - int y = clipRectY; - int w = clipRectW; - int h = clipRectH; - if (clipRectW && clipRectH) - { - g->SetClipRect(x, y, w, h); // old cliprect is now in x, y, w, h - } + auto rect = ClipRect; + if (ClipRect.Size().X && ClipRect.Size().Y) + g->SwapClipRect(rect); // old cliprect is now in rect + int totalColour = Appearance.BackgroundInactive.Blue + (3*Appearance.BackgroundInactive.Green) + (2*Appearance.BackgroundInactive.Red); if (Appearance.GetTexture()) { - g->draw_image(Appearance.GetTexture(), screenPos.X+2, screenPos.Y+2, 255); + auto *tex = Appearance.GetTexture(); + g->BlendImage(tex->Data(), 255, RectSized(screenPos + Vec2{ 2, 2 }, tex->Size())); } else { - g->fillrect(screenPos.X+2, screenPos.Y+2, Size.X-4, Size.Y-4, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, Appearance.BackgroundInactive.Alpha); + g->BlendFilledRect(RectSized(screenPos + Vec2{ 2, 2 }, Size - Vec2{ 4, 4 }), Appearance.BackgroundInactive); } if (isMouseInside && currentSelection == -1) { - g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, Appearance.BorderActive.Red, Appearance.BorderActive.Green, Appearance.BorderActive.Blue, Appearance.BorderActive.Alpha); + g->BlendRect(RectSized(screenPos, Size), Appearance.BorderActive); } else { - g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, Appearance.BorderInactive.Red, Appearance.BorderInactive.Green, Appearance.BorderInactive.Blue, Appearance.BorderInactive.Alpha); + g->BlendRect(RectSized(screenPos, Size), Appearance.BorderInactive); } if (Favorite::Ref().IsFavorite(toolIdentifier)) { - g->drawtext(screenPos.X, screenPos.Y, 0xE068, Appearance.BorderFavorite.Red, Appearance.BorderFavorite.Green, Appearance.BorderFavorite.Blue, Appearance.BorderFavorite.Alpha); + g->BlendText(screenPos, 0xE068, Appearance.BorderFavorite); } if (totalColour<544) { - g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, buttonDisplayText, 255, 255, 255, 255); + g->BlendText(screenPos + textPosition, buttonDisplayText, 0xFFFFFF_rgb .WithAlpha(255)); } else { - g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, buttonDisplayText, 0, 0, 0, 255); - } - if (clipRectW && clipRectH) - { - g->SetClipRect(x, y, w, h); // apply old clip rect + g->BlendText(screenPos + textPosition, buttonDisplayText, 0x000000_rgb .WithAlpha(255)); } + + if (ClipRect.Size().X && ClipRect.Size().Y) + g->SwapClipRect(rect); // apply old clip rect } void ToolButton::SetSelectionState(int state) diff --git a/src/gui/game/ToolButton.h b/src/gui/game/ToolButton.h index e6d99ce72..18687a93e 100644 --- a/src/gui/game/ToolButton.h +++ b/src/gui/game/ToolButton.h @@ -1,6 +1,4 @@ -#ifndef TOOLBUTTON_H_ -#define TOOLBUTTON_H_ - +#pragma once #include "gui/interface/Button.h" class Tool; @@ -18,10 +16,5 @@ public: void SetSelectionState(int state); int GetSelectionState(); Tool *tool; - int clipRectX = 0; - int clipRectY = 0; - int clipRectW = 0; - int clipRectH = 0; + Rect ClipRect = RectSized(Vec2::Zero, Vec2::Zero); }; - -#endif /* TOOLBUTTON_H_ */ diff --git a/src/gui/game/TriangleBrush.h b/src/gui/game/TriangleBrush.h index 43a300d40..0aafa00ee 100644 --- a/src/gui/game/TriangleBrush.h +++ b/src/gui/game/TriangleBrush.h @@ -1,28 +1,17 @@ -/* - * TriangleBrush.h - * - * Created on: Jan 26, 2012 - * Author: Savely Skresanov - */ - -#ifndef TRIANGLEBRUSH_H_ -#define TRIANGLEBRUSH_H_ - -#include +#pragma once #include "Brush.h" +#include class TriangleBrush: public Brush { public: - TriangleBrush(ui::Point size_): - Brush(size_) + virtual ~TriangleBrush() override = default; + + std::unique_ptr GenerateBitmap() const override { - SetRadius(size_); - }; - void GenerateBitmap() override - { - delete[] bitmap; - bitmap = new unsigned char[size.X*size.Y]; + ui::Point size = radius * 2 + Vec2{ 1, 1 }; + auto bitmap = std::make_unique(size.X * size.Y); + int rx = radius.X; int ry = radius.Y; for(int x = -rx; x <= rx; x++) @@ -39,7 +28,11 @@ public: } } } + return bitmap; + } + + std::unique_ptr Clone() const override + { + return std::make_unique(*this); } }; - -#endif /* TRIANGLEBRUSH_H_ */ diff --git a/src/gui/interface/Appearance.cpp b/src/gui/interface/Appearance.cpp index fef5095a9..b17cf5070 100644 --- a/src/gui/interface/Appearance.cpp +++ b/src/gui/interface/Appearance.cpp @@ -1,29 +1,30 @@ -#include #include "Appearance.h" #include "graphics/Graphics.h" +#include namespace ui { Appearance::Appearance(): - texture(NULL), + texture(nullptr), VerticalAlign(AlignMiddle), HorizontalAlign(AlignCentre), - BackgroundHover(20, 20, 20), - BackgroundInactive(0, 0, 0), - BackgroundActive(255, 255, 255), - BackgroundDisabled(10, 10, 10), + BackgroundHover(0x141414_rgb .WithAlpha(0xFF)), + BackgroundInactive(0x000000_rgb .WithAlpha(0xFF)), + BackgroundActive(0xFFFFFF_rgb .WithAlpha(0xFF)), + BackgroundDisabled(0x0A0A0A_rgb .WithAlpha(0xFF)), - TextHover(255, 255, 255), - TextInactive(255, 255, 255), - TextActive(0, 0, 0), - TextDisabled(100, 100, 100), + TextHover(0xFFFFFF_rgb .WithAlpha(0xFF)), + TextInactive(0xFFFFFF_rgb .WithAlpha(0xFF)), + TextActive(0x000000_rgb .WithAlpha(0xFF)), + TextDisabled(0x646464_rgb .WithAlpha(0xFF)), - BorderHover(255, 255, 255), - BorderInactive(200, 200, 200), - BorderActive(235, 235, 235), - BorderDisabled(100, 100, 100), + BorderHover(0xFFFFFF_rgb .WithAlpha(0xFF)), + BorderInactive(0xC8C8C8_rgb .WithAlpha(0xFF)), + BorderActive(0xEBEBEB_rgb .WithAlpha(0xFF)), + BorderFavorite(0xFFFF00_rgb .WithAlpha(0xFF)), + BorderDisabled(0x646464_rgb .WithAlpha(0xFF)), Margin(1, 4), Border(1), @@ -31,23 +32,13 @@ namespace ui icon(NoIcon) {} - VideoBuffer * Appearance::GetTexture() + VideoBuffer const *Appearance::GetTexture() { - return texture; + return texture.get(); } - void Appearance::SetTexture(VideoBuffer * texture) + void Appearance::SetTexture(std::unique_ptr texture) { - delete this->texture; - if(texture) - this->texture = new VideoBuffer(texture); - else - this->texture = NULL; + this->texture = std::move(texture); } - - Appearance::~Appearance() - { - delete texture; - } - } diff --git a/src/gui/interface/Appearance.h b/src/gui/interface/Appearance.h index 2f6a7c7e7..a82fd3ce6 100644 --- a/src/gui/interface/Appearance.h +++ b/src/gui/interface/Appearance.h @@ -1,7 +1,5 @@ -#ifndef APPEARANCE_H_ -#define APPEARANCE_H_ -#include "Config.h" - +#pragma once +#include #include "Border.h" #include "Colour.h" #include "graphics/Icons.h" @@ -12,7 +10,8 @@ namespace ui class Appearance { private: - VideoBuffer * texture; + std::shared_ptr texture; + public: enum HorizontalAlignment { @@ -49,12 +48,9 @@ namespace ui Icon icon; - VideoBuffer * GetTexture(); - void SetTexture(VideoBuffer * texture); + VideoBuffer const *GetTexture(); + void SetTexture(std::unique_ptr texture); Appearance(); - ~Appearance(); }; } - -#endif diff --git a/src/gui/interface/AvatarButton.cpp b/src/gui/interface/AvatarButton.cpp index 6409457f3..1bef78953 100644 --- a/src/gui/interface/AvatarButton.cpp +++ b/src/gui/interface/AvatarButton.cpp @@ -1,13 +1,11 @@ -#include -#include - #include "Button.h" #include "AvatarButton.h" #include "Format.h" #include "graphics/Graphics.h" #include "ContextMenu.h" -#include "Keys.h" -#include "Mouse.h" +#include "Config.h" +#include +#include namespace ui { @@ -19,21 +17,27 @@ AvatarButton::AvatarButton(Point position, Point size, ByteString username): } -void AvatarButton::OnResponse(std::unique_ptr Avatar) -{ - avatar = std::move(Avatar); -} - void AvatarButton::Tick(float dt) { if (!avatar && !tried && name.size() > 0) { tried = true; - RequestSetup(SCHEME STATICSERVER "/avatars/" + name + ".png", Size.X, Size.Y); - RequestStart(); + imageRequest = std::make_unique(ByteString::Build(SCHEME, STATICSERVER, "/avatars/", name, ".png"), Size); + imageRequest->Start(); } - RequestPoll(); + if (imageRequest && imageRequest->CheckDone()) + { + try + { + avatar = imageRequest->Finish(); + } + catch (const http::RequestError &ex) + { + // Nothing, oh well. + } + imageRequest.reset(); + } } void AvatarButton::Draw(const Point& screenPos) @@ -42,7 +46,8 @@ void AvatarButton::Draw(const Point& screenPos) if(avatar) { - g->draw_image(avatar.get(), screenPos.X, screenPos.Y, 255); + auto *tex = avatar.get(); + g->BlendImage(tex->Data(), 255, RectSized(screenPos, tex->Size())); } } diff --git a/src/gui/interface/AvatarButton.h b/src/gui/interface/AvatarButton.h index 0ac7c1241..4558354a7 100644 --- a/src/gui/interface/AvatarButton.h +++ b/src/gui/interface/AvatarButton.h @@ -1,20 +1,17 @@ -#ifndef AVATARBUTTON_H_ -#define AVATARBUTTON_H_ - +#pragma once #include "common/String.h" #include "Component.h" #include "graphics/Graphics.h" #include "gui/interface/Colour.h" #include "client/http/ImageRequest.h" -#include "client/http/RequestMonitor.h" #include #include namespace ui { -class AvatarButton : public Component, public http::RequestMonitor +class AvatarButton : public Component { std::unique_ptr avatar; ByteString name; @@ -26,6 +23,8 @@ class AvatarButton : public Component, public http::RequestMonitor imageRequest; + public: AvatarButton(Point position, Point size, ByteString username); virtual ~AvatarButton() = default; @@ -41,8 +40,6 @@ public: void Draw(const Point& screenPos) override; void Tick(float dt) override; - void OnResponse(std::unique_ptr avatar) override; - void DoAction(); void SetUsername(ByteString username) { name = username; } @@ -52,5 +49,3 @@ protected: bool isMouseInside, isButtonDown; }; } -#endif /* AVATARBUTTON_H_ */ - diff --git a/src/gui/interface/Border.h b/src/gui/interface/Border.h index ff55e1a03..39ed05d33 100644 --- a/src/gui/interface/Border.h +++ b/src/gui/interface/Border.h @@ -5,11 +5,7 @@ namespace ui struct Border { -#if ENABLE_FLOAT_UI -# define BORDER_T float -#else -# define BORDER_T int -#endif + using BORDER_T = int; BORDER_T Top; BORDER_T Right; diff --git a/src/gui/interface/Button.cpp b/src/gui/interface/Button.cpp index e9e099b0d..81155cc36 100644 --- a/src/gui/interface/Button.cpp +++ b/src/gui/interface/Button.cpp @@ -25,10 +25,10 @@ void Button::TextPosition(String ButtonText) buttonDisplayText = ButtonText; if(buttonDisplayText.length()) { - if(Graphics::textwidth(buttonDisplayText) > Size.X - (Appearance.icon? 22 : 0)) + if (Graphics::TextSize(buttonDisplayText).X - 1 > Size.X - (Appearance.icon ? 22 : 0)) { - int position = Graphics::textwidthx(buttonDisplayText, Size.X - (Appearance.icon? 38 : 22)); - buttonDisplayText = buttonDisplayText.erase(position, buttonDisplayText.length()-position); + auto it = Graphics::TextFit(buttonDisplayText, Size.X - (Appearance.icon ? 38 : 22)); + buttonDisplayText.erase(it, buttonDisplayText.end()); buttonDisplayText += "..."; } } @@ -113,21 +113,21 @@ void Button::Draw(const Point& screenPos) } bgColour = Appearance.BackgroundInactive; - g->fillrect(Position.X+1, Position.Y+1, Size.X-2, Size.Y-2, backgroundColour.Red, backgroundColour.Green, backgroundColour.Blue, backgroundColour.Alpha); + g->BlendFilledRect(RectSized(Position + Vec2{ 1, 1 }, Size - Vec2{ 2, 2 }), backgroundColour); if(Appearance.Border == 1) - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + g->BlendRect(RectSized(Position, Size), borderColour); else { if(Appearance.Border.Top) - g->draw_line(Position.X, Position.Y, Position.X+Size.X-1, Position.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + g->BlendLine(Position + Vec2{ 0 , 0 }, Position + Vec2{ Size.X-1, 0 }, borderColour); if(Appearance.Border.Bottom) - g->draw_line(Position.X, Position.Y+Size.Y-1, Position.X+Size.X-1, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + g->BlendLine(Position + Vec2{ 0 , Size.Y-1 }, Position + Vec2{ Size.X-1, Size.Y-1 }, borderColour); if(Appearance.Border.Left) - g->draw_line(Position.X, Position.Y, Position.X, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + g->BlendLine(Position + Vec2{ 0 , 0 }, Position + Vec2{ 0, Size.Y-1 }, borderColour); if(Appearance.Border.Right) - g->draw_line(Position.X+Size.X-1, Position.Y, Position.X+Size.X-1, Position.Y+Size.Y-1, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + g->BlendLine(Position + Vec2{ Size.X-1, 0 }, Position + Vec2{ Size.X-1, Size.Y-1 }, borderColour); } - g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, buttonDisplayText, textColour.Red, textColour.Green, textColour.Blue, textColour.Alpha); + g->BlendText(Position + textPosition, buttonDisplayText, textColour); bool iconInvert = (backgroundColour.Blue + (3*backgroundColour.Green) + (2*backgroundColour.Red))>544?true:false; diff --git a/src/gui/interface/Button.h b/src/gui/interface/Button.h index cc7bc1f6f..ca1c8af45 100644 --- a/src/gui/interface/Button.h +++ b/src/gui/interface/Button.h @@ -1,9 +1,6 @@ -#ifndef BUTTON_H_ -#define BUTTON_H_ - +#pragma once #include "common/String.h" #include "Component.h" - #include namespace ui @@ -54,4 +51,3 @@ protected: ButtonAction actionCallback; }; } -#endif /* BUTTON_H_ */ diff --git a/src/gui/interface/Checkbox.cpp b/src/gui/interface/Checkbox.cpp index 42e23d14e..9a423e8e8 100644 --- a/src/gui/interface/Checkbox.cpp +++ b/src/gui/interface/Checkbox.cpp @@ -76,22 +76,22 @@ void Checkbox::Draw(const Point& screenPos) Graphics * g = GetGraphics(); if(checked) { - g->fillrect(screenPos.X+5, screenPos.Y+5, 6, 6, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(screenPos + Vec2{ 5, 5 }, Vec2{ 6, 6 }), 0xFFFFFF_rgb); } if(isMouseOver) { - g->drawrect(screenPos.X+2, screenPos.Y+2, 12, 12, 255, 255, 255, 255); - g->fillrect(screenPos.X+5, screenPos.Y+5, 6, 6, 255, 255, 255, 170); + g->DrawRect(RectSized(screenPos + Vec2{ 2, 2 }, Vec2{ 12, 12 }), 0xFFFFFF_rgb); + g->BlendFilledRect(RectSized(screenPos + Vec2{ 5, 5 }, Vec2{ 6, 6 }), 0xFFFFFF_rgb .WithAlpha(170)); if (!Appearance.icon) - g->drawtext(screenPos.X+18, screenPos.Y+4, text, 255, 255, 255, 255); + g->BlendText(screenPos + Vec2{ 18, 4 }, text, 0xFFFFFF_rgb .WithAlpha(255)); else g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon, 255); } else { - g->drawrect(screenPos.X+2, screenPos.Y+2, 12, 12, 255, 255, 255, 200); + g->BlendRect(RectSized(screenPos + Vec2{ 2, 2 }, Vec2{ 12, 12 }), 0xFFFFFF_rgb .WithAlpha(200)); if (!Appearance.icon) - g->drawtext(screenPos.X+18, screenPos.Y+4, text, 255, 255, 255, 200); + g->BlendText(screenPos + Vec2{ 18, 4 }, text, 0xFFFFFF_rgb .WithAlpha(200)); else g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon, 200); } diff --git a/src/gui/interface/Checkbox.h b/src/gui/interface/Checkbox.h index 2fba1a924..0be16a3cb 100644 --- a/src/gui/interface/Checkbox.h +++ b/src/gui/interface/Checkbox.h @@ -1,9 +1,6 @@ -#ifndef CHECKBOX_H_ -#define CHECKBOX_H_ - +#pragma once #include "common/String.h" #include "Component.h" - #include namespace ui @@ -38,5 +35,3 @@ public: void SetChecked(bool checked_) { checked = checked_; } }; } - -#endif /* CHECKBOX_H_ */ diff --git a/src/gui/interface/Colour.h b/src/gui/interface/Colour.h index 194b9c9d3..06ccaf137 100644 --- a/src/gui/interface/Colour.h +++ b/src/gui/interface/Colour.h @@ -1,24 +1,8 @@ -#ifndef COLOUR_H -#define COLOUR_H +#pragma once + +#include "graphics/Pixel.h" namespace ui { -class Colour -{ -public: - unsigned char Red, Green, Blue, Alpha; - Colour(unsigned char red, unsigned char green, unsigned char blue): - Red(red), Green(green), Blue(blue), Alpha(255) - { - } - Colour(unsigned char red, unsigned char green, unsigned char blue, unsigned char alpha): - Red(red), Green(green), Blue(blue), Alpha(alpha) - { - } - Colour() - { - } -}; + using Colour = RGBA; } - -#endif diff --git a/src/gui/interface/Component.cpp b/src/gui/interface/Component.cpp index f384c2101..735e62899 100644 --- a/src/gui/interface/Component.cpp +++ b/src/gui/interface/Component.cpp @@ -8,23 +8,6 @@ using namespace ui; -Component::Component(Window* parent_state): - parentstate_(parent_state), - _parent(NULL), - drawn(false), - textPosition(0, 0), - textSize(0, 0), - iconPosition(0, 0), - menu(NULL), - Position(Point(0,0)), - Size(Point(0,0)), - Enabled(true), - Visible(true), - DoesTextInput(false) -{ - -} - Component::Component(Point position, Point size): parentstate_(0), _parent(NULL), @@ -42,23 +25,6 @@ Component::Component(Point position, Point size): } -Component::Component(): - parentstate_(NULL), - _parent(NULL), - drawn(false), - textPosition(0, 0), - textSize(0, 0), - iconPosition(0, 0), - menu(NULL), - Position(Point(0,0)), - Size(Point(0,0)), - Enabled(true), - Visible(true), - DoesTextInput(false) -{ - -} - void Component::Refresh() { drawn = false; @@ -69,9 +35,8 @@ void Component::TextPosition(String displayText) textPosition = ui::Point(0, 0); - int textWidth, textHeight = 10; - Graphics::textsize(displayText, textWidth, textHeight); - textSize.X = textWidth; textSize.Y = textHeight; + textSize = Graphics::TextSize(displayText); + int textWidth = textSize.X, textHeight = textSize.Y; textHeight-=3; textWidth-=1; if(Appearance.icon) diff --git a/src/gui/interface/Component.h b/src/gui/interface/Component.h index 2845e6736..20c1d875e 100644 --- a/src/gui/interface/Component.h +++ b/src/gui/interface/Component.h @@ -1,6 +1,4 @@ #pragma once -#include "Config.h" - #include "common/String.h" #include "Appearance.h" #include "Point.h" @@ -31,9 +29,7 @@ namespace ui Graphics * GetGraphics(); public: - Component(Window* parent_state); Component(Point position, Point size); - Component(); virtual ~Component(); void* UserData; diff --git a/src/gui/interface/ContextMenu.cpp b/src/gui/interface/ContextMenu.cpp index 5222de132..a1206cb34 100644 --- a/src/gui/interface/ContextMenu.cpp +++ b/src/gui/interface/ContextMenu.cpp @@ -1,8 +1,7 @@ #include "ContextMenu.h" - #include "graphics/Graphics.h" - #include "common/tpt-minmax.h" +#include "SimulationConfig.h" using namespace ui; @@ -95,6 +94,6 @@ void ContextMenu::AddItem(ContextMenuItem item) void ContextMenu::OnDraw() { Graphics * g = GetGraphics(); - g->fillrect(Position.X, Position.Y, Size.X, Size.Y, 100, 100, 100, 255); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, Appearance.BackgroundInactive.Red, Appearance.BackgroundInactive.Green, Appearance.BackgroundInactive.Blue, Appearance.BackgroundInactive.Alpha); + g->DrawFilledRect(RectSized(Position, Size), 0x646464_rgb); + g->BlendRect(RectSized(Position, Size), Appearance.BackgroundInactive); } diff --git a/src/gui/interface/ContextMenu.h b/src/gui/interface/ContextMenu.h index b6dbd1384..49e5d1d31 100644 --- a/src/gui/interface/ContextMenu.h +++ b/src/gui/interface/ContextMenu.h @@ -1,6 +1,4 @@ -#ifndef The_Powder_Toy_ContextMenu_h -#define The_Powder_Toy_ContextMenu_h - +#pragma once #include "Window.h" #include "Appearance.h" #include "Button.h" @@ -36,5 +34,3 @@ public: void OnMouseDown(int x, int y, unsigned button) override; }; } - -#endif diff --git a/src/gui/interface/CopyTextButton.cpp b/src/gui/interface/CopyTextButton.cpp index 70a21109e..6bf14f8ac 100644 --- a/src/gui/interface/CopyTextButton.cpp +++ b/src/gui/interface/CopyTextButton.cpp @@ -4,7 +4,7 @@ #include "gui/Style.h" #include "Label.h" -#include "PowderToy.h" +#include "PowderToySDL.h" namespace ui { diff --git a/src/gui/interface/CopyTextButton.h b/src/gui/interface/CopyTextButton.h index 56b0de053..83b33b6a6 100644 --- a/src/gui/interface/CopyTextButton.h +++ b/src/gui/interface/CopyTextButton.h @@ -1,6 +1,4 @@ -#ifndef COPYTEXTBUTTON_H -#define COPYTEXTBUTTON_H - +#pragma once #include "Button.h" namespace ui @@ -18,5 +16,3 @@ public: void OnMouseLeave(int x, int y) override; }; } -#endif /* COPYTEXTBUTTON_H */ - diff --git a/src/gui/interface/DirectionSelector.cpp b/src/gui/interface/DirectionSelector.cpp index 389e540ac..6fc83321b 100644 --- a/src/gui/interface/DirectionSelector.cpp +++ b/src/gui/interface/DirectionSelector.cpp @@ -113,23 +113,24 @@ void DirectionSelector::Draw(const ui::Point& screenPos) { Graphics * g = GetGraphics(); auto handleTrackRadius = radius + handleRadius; - ui::Point center = screenPos + handleTrackRadius; + ui::Point center = screenPos + Vec2{ handleTrackRadius, handleTrackRadius }; - g->fillcircle(center.X, center.Y, handleTrackRadius, handleTrackRadius, backgroundColor.Red, backgroundColor.Green, backgroundColor.Blue, backgroundColor.Alpha); - g->drawcircle(center.X, center.Y, handleTrackRadius, handleTrackRadius, borderColor.Red, borderColor.Green, borderColor.Blue, borderColor.Alpha); + g->BlendFilledEllipse(center, { handleTrackRadius, handleTrackRadius }, backgroundColor); + g->BlendEllipse(center, { handleTrackRadius, handleTrackRadius }, borderColor); for (auto &point : snapPoints) { - g->fillrect( - (center.X + point.offset.X) - snapPointRadius, - (center.Y + point.offset.Y) - snapPointRadius, - snapPointRadius * 2 + 1, snapPointRadius * 2 + 1, - snapPointColor.Red, snapPointColor.Green, snapPointColor.Blue, altDown ? (int)(snapPointColor.Alpha / 2) : snapPointColor.Alpha + g->BlendFilledRect( + RectBetween( + center + point.offset - Vec2{ snapPointRadius, snapPointRadius }, + center + point.offset + Vec2{ snapPointRadius, snapPointRadius } + ), + snapPointColor.NoAlpha().WithAlpha(altDown ? (int)(snapPointColor.Alpha / 2) : snapPointColor.Alpha) ); } - g->fillcircle(center.X + value.offset.X, center.Y + value.offset.Y, handleRadius, handleRadius, foregroundColor.Red, foregroundColor.Green, foregroundColor.Blue, (mouseHover || mouseDown) ? std::min((int)(foregroundColor.Alpha * 1.5f), 255) : foregroundColor.Alpha); - g->drawcircle(center.X + value.offset.X, center.Y + value.offset.Y, handleRadius, handleRadius, borderColor.Red, borderColor.Green, borderColor.Blue, borderColor.Alpha); + g->BlendFilledEllipse(center + value.offset, { handleRadius, handleRadius }, foregroundColor.NoAlpha().WithAlpha((mouseHover || mouseDown) ? std::min(int(foregroundColor.Alpha * 1.5f), 255) : foregroundColor.Alpha)); + g->BlendEllipse(center + value.offset, { handleRadius, handleRadius }, borderColor); } void DirectionSelector::OnMouseMoved(int x, int y, int dx, int dy) diff --git a/src/gui/interface/DirectionSelector.h b/src/gui/interface/DirectionSelector.h index e3dff2ef6..a3745f1c7 100644 --- a/src/gui/interface/DirectionSelector.h +++ b/src/gui/interface/DirectionSelector.h @@ -1,12 +1,8 @@ -#ifndef DIRECTIONSELECTOR_H_ -#define DIRECTIONSELECTOR_H_ - +#pragma once #include "Component.h" #include "Colour.h" #include "graphics/Graphics.h" - #include - #include #include #include @@ -93,4 +89,3 @@ public: }; } /* namespace ui */ -#endif /* DIRECTIONSELECTOR_H_ */ diff --git a/src/gui/interface/DropDown.cpp b/src/gui/interface/DropDown.cpp index 9c40964ea..c1da64d11 100644 --- a/src/gui/interface/DropDown.cpp +++ b/src/gui/interface/DropDown.cpp @@ -15,7 +15,7 @@ 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, 1+dropDown->options.size()*16)), + Window(dropDown->GetScreenPos() + ui::Point(-1, -1 - dropDown->optionIndex * 16), ui::Point(dropDown->Size.X+2, 2+dropDown->options.size()*16)), dropDown(dropDown), appearance(dropDown->Appearance) { @@ -39,7 +39,7 @@ public: void OnDraw() override { Graphics * g = GetGraphics(); - g->clearrect(Position.X, Position.Y, Size.X, Size.Y); + g->DrawFilledRect(RectSized(Position, Size), 0x000000_rgb); } void setOption(String option) { @@ -98,10 +98,10 @@ void DropDown::Draw(const Point& screenPos) backgroundColour = Appearance.BackgroundInactive; } - g->fillrect(Position.X-1, Position.Y-1, Size.X+2, Size.Y+2, backgroundColour.Red, backgroundColour.Green, backgroundColour.Blue, backgroundColour.Alpha); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, borderColour.Red, borderColour.Green, borderColour.Blue, borderColour.Alpha); + g->BlendFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), backgroundColour); + g->BlendRect(RectSized(Position, Size), borderColour); if(optionIndex!=-1) - g->drawtext(Position.X+textPosition.X, Position.Y+textPosition.Y, options[optionIndex].first, textColour.Red, textColour.Green, textColour.Blue, textColour.Alpha); + g->BlendText(Position + textPosition, options[optionIndex].first, textColour); } void DropDown::OnMouseEnter(int x, int y) diff --git a/src/gui/interface/DropDown.h b/src/gui/interface/DropDown.h index c1c97a23c..c583cd289 100644 --- a/src/gui/interface/DropDown.h +++ b/src/gui/interface/DropDown.h @@ -1,9 +1,6 @@ -#ifndef DROPDOWN_H_ -#define DROPDOWN_H_ - -#include +#pragma once #include "Component.h" - +#include #include namespace ui { @@ -42,4 +39,3 @@ public: }; } /* namespace ui */ -#endif /* DROPDOWN_H_ */ diff --git a/src/gui/interface/Engine.cpp b/src/gui/interface/Engine.cpp index af0f8178e..57b27900e 100644 --- a/src/gui/interface/Engine.cpp +++ b/src/gui/interface/Engine.cpp @@ -1,17 +1,12 @@ #include "Engine.h" - +#include "PowderToySDL.h" +#include "Window.h" +#include "common/platform/Platform.h" +#include "graphics/Graphics.h" +#include "gui/dialogues/ConfirmPrompt.h" #include #include -#include "Config.h" -#include "PowderToy.h" -#include "Window.h" - -#include "common/Platform.h" -#include "graphics/Graphics.h" - -#include "gui/dialogues/ConfirmPrompt.h" - using namespace ui; Engine::Engine(): @@ -22,7 +17,6 @@ Engine::Engine(): FrameIndex(0), altFullscreen(false), resizable(false), - lastBuffer(NULL), state_(NULL), windowTargetPosition(0, 0), break_(false), @@ -32,31 +26,26 @@ Engine::Engine(): mousex_(0), mousey_(0), mousexp_(0), - mouseyp_(0), - maxWidth(0), - maxHeight(0) + mouseyp_(0) { + SetFps(FpsLimit); // populate dt with whatever that makes any sort of sense } Engine::~Engine() { delete state_; //Dispose of any Windows. - while(!windows.empty()) + while (!windows.empty()) { delete windows.top(); windows.pop(); } - free(lastBuffer); } -void Engine::Begin(int width, int height) +void Engine::Begin() { //engine is now ready running_ = true; - - width_ = width; - height_ = height; } void Engine::Break() @@ -84,16 +73,15 @@ void Engine::ConfirmExit() void Engine::ShowWindow(Window * window) { - windowOpenState = 0; if (state_) ignoreEvents = true; if(window->Position.X==-1) { - window->Position.X = (width_-window->Size.X)/2; + window->Position.X = (g->Size().X - window->Size.X) / 2; } if(window->Position.Y==-1) { - window->Position.Y = (height_-window->Size.Y)/2; + window->Position.Y = (g->Size().Y - window->Size.Y) / 2; } /*if(window->Position.Y > 0) { @@ -102,13 +90,8 @@ void Engine::ShowWindow(Window * window) }*/ if(state_) { - if(lastBuffer) - { - prevBuffers.push(lastBuffer); - } - lastBuffer = (pixel*)malloc((width_ * height_) * PIXELSIZE); - - memcpy(lastBuffer, g->vid, (width_ * height_) * PIXELSIZE); + frozenGraphics.emplace(FrozenGraphics{0, std::make_unique(g->Size().X * g->Size().Y)}); + std::copy_n(g->Data(), g->Size().X * g->Size().Y, frozenGraphics.top().screen.get()); windows.push(state_); mousePositions.push(ui::Point(mousex_, mousey_)); @@ -124,16 +107,7 @@ int Engine::CloseWindow() { if(!windows.empty()) { - if (lastBuffer) - { - free(lastBuffer); - lastBuffer = NULL; - } - if(!prevBuffers.empty()) - { - lastBuffer = prevBuffers.top(); - prevBuffers.pop(); - } + frozenGraphics.pop(); state_ = windows.top(); windows.pop(); @@ -172,17 +146,6 @@ int Engine::CloseWindow() } }*/ -void Engine::SetSize(int width, int height) -{ - width_ = width; - height_ = height; -} - -void Engine::SetMaxSize(int width, int height) -{ - maxWidth = width; - maxHeight = height; -} void Engine::Tick() { @@ -211,13 +174,21 @@ void Engine::Tick() void Engine::Draw() { - if(lastBuffer && !(state_ && state_->Position.X == 0 && state_->Position.Y == 0 && state_->Size.X == width_ && state_->Size.Y == height_)) + if (!frozenGraphics.empty() && !(state_ && RectSized(state_->Position, state_->Size) == g->Size().OriginRect())) { - g->Clear(); - memcpy(g->vid, lastBuffer, (width_ * height_) * PIXELSIZE); - if(windowOpenState < 20) - windowOpenState++; - g->fillrect(0, 0, width_, height_, 0, 0, 0, int(255-std::pow(.98, windowOpenState)*255)); + auto &frozen = frozenGraphics.top(); + std::copy_n(frozen.screen.get(), g->Size().X * g->Size().Y, g->Data()); + if (frozen.fadeTicks <= maxFadeTicks) + { + // from 0x00 at 0 to about 0x54 at 20 + auto alpha = uint8_t((1 - std::pow(0.98, frozen.fadeTicks)) * 0xFF); + g->BlendFilledRect(g->Size().OriginRect(), 0x000000_rgb .WithAlpha(alpha)); + } + // If this is the last frame in the fade, save what the faded image looks like + if (frozen.fadeTicks == maxFadeTicks) + std::copy_n(g->Data(), g->Size().X * g->Size().Y, frozen.screen.get()); + if (frozen.fadeTicks <= maxFadeTicks) + frozen.fadeTicks++; } else { @@ -326,11 +297,6 @@ void Engine::onMouseWheel(int x, int y, int delta) state_->DoMouseWheel(x, y, delta); } -void Engine::onResize(int newWidth, int newHeight) -{ - SetSize(newWidth, newHeight); -} - void Engine::onClose() { if (state_) diff --git a/src/gui/interface/Engine.h b/src/gui/interface/Engine.h index d27b52147..3e8bbe69d 100644 --- a/src/gui/interface/Engine.h +++ b/src/gui/interface/Engine.h @@ -1,8 +1,8 @@ #pragma once - +#include #include #include "common/String.h" -#include "common/Singleton.h" +#include "common/ExplicitSingleton.h" #include "graphics/Pixel.h" #include "gui/interface/Point.h" @@ -16,7 +16,7 @@ namespace ui * Controls the User Interface. * Send user inputs to the Engine and the appropriate controls and components will interact. */ - class Engine: public Singleton + class Engine: public ExplicitSingleton { public: Engine(); @@ -34,15 +34,13 @@ namespace ui void onKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt); void onTextInput(String text); void onTextEditing(String text, int start); - void onResize(int newWidth, int newHeight); void onClose(); void onFileDrop(ByteString filename); - void Begin(int width, int height); + void Begin(); inline bool Running() { return running_; } inline bool Broken() { return break_; } inline long unsigned int LastTick() { return lastTick; } - inline void LastTick(long unsigned int tick) { lastTick = tick; } void Exit(); void ConfirmExit(); void Break(); @@ -72,14 +70,6 @@ namespace ui inline int GetMouseButton() { return mouseb_; } inline int GetMouseX() { return mousex_; } inline int GetMouseY() { return mousey_; } - inline int GetWidth() { return width_; } - inline int GetHeight() { return height_; } - inline int GetMaxWidth() { return maxWidth; } - inline int GetMaxHeight() { return maxHeight; } - - void SetMaxSize(int width, int height); - - inline void SetSize(int width, int height); void StartTextInput(); void StopTextInput(); @@ -92,6 +82,7 @@ namespace ui int drawingFrequencyLimit; Graphics * g; int Scale; + bool GraveExitsConsole; bool Fullscreen; unsigned int FrameIndex; @@ -104,16 +95,23 @@ namespace ui float dt; float fps; - pixel * lastBuffer; - std::stack prevBuffers; std::stack windows; std::stack mousePositions; //Window* statequeued_; Window* state_; Point windowTargetPosition; - int windowOpenState; bool ignoreEvents = false; + // saved appearances of windows that are in the backround and + // thus are not currently being redrawn + struct FrozenGraphics + { + int fadeTicks; + std::unique_ptr screen; + }; + constexpr static int maxFadeTicks = 20; + std::stack frozenGraphics; + bool running_; bool break_; bool FastQuit; @@ -124,11 +122,6 @@ namespace ui int mousey_; int mousexp_; int mouseyp_; - int width_; - int height_; - - int maxWidth; - int maxHeight; String textEditingBuf; diff --git a/src/gui/interface/Keys.h b/src/gui/interface/Keys.h deleted file mode 100644 index 781c3bda6..000000000 --- a/src/gui/interface/Keys.h +++ /dev/null @@ -1,328 +0,0 @@ -/* - SDL - Simple DirectMedia Layer - Copyright (C) 1997-2012 Sam Lantinga - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - - Sam Lantinga - slouken@libsdl.org -*/ - -#include "SDLCompat.h" -#define _SDL_keysym_h -#ifndef _SDL_keysym_h -#define _SDL_keysym_h - -/** What we really want is a mapping of every raw key on the keyboard. - * To support international keyboards, we use the range 0xA1 - 0xFF - * as international virtual keycodes. We'll follow in the footsteps of X11... - * @brief The names of the keys - */ -typedef enum { - /** @name ASCII mapped keysyms - * The keyboard syms have been cleverly chosen to map to ASCII - */ - /*@{*/ - SDLK_UNKNOWN = 0, - SDLK_FIRST = 0, - SDLK_BACKSPACE = 8, - SDLK_TAB = 9, - SDLK_CLEAR = 12, - SDLK_RETURN = 13, - SDLK_PAUSE = 19, - SDLK_ESCAPE = 27, - SDLK_SPACE = 32, - SDLK_EXCLAIM = 33, - SDLK_QUOTEDBL = 34, - SDLK_HASH = 35, - SDLK_DOLLAR = 36, - SDLK_AMPERSAND = 38, - SDLK_QUOTE = 39, - SDLK_LEFTPAREN = 40, - SDLK_RIGHTPAREN = 41, - SDLK_ASTERISK = 42, - SDLK_PLUS = 43, - SDLK_COMMA = 44, - SDLK_MINUS = 45, - SDLK_PERIOD = 46, - SDLK_SLASH = 47, - SDLK_0 = 48, - SDLK_1 = 49, - SDLK_2 = 50, - SDLK_3 = 51, - SDLK_4 = 52, - SDLK_5 = 53, - SDLK_6 = 54, - SDLK_7 = 55, - SDLK_8 = 56, - SDLK_9 = 57, - SDLK_COLON = 58, - SDLK_SEMICOLON = 59, - SDLK_LESS = 60, - SDLK_EQUALS = 61, - SDLK_GREATER = 62, - SDLK_QUESTION = 63, - SDLK_AT = 64, - /* - Skip uppercase letters - */ - SDLK_LEFTBRACKET = 91, - SDLK_BACKSLASH = 92, - SDLK_RIGHTBRACKET = 93, - SDLK_CARET = 94, - SDLK_UNDERSCORE = 95, - SDLK_BACKQUOTE = 96, - SDLK_a = 97, - SDLK_b = 98, - SDLK_c = 99, - SDLK_d = 100, - SDLK_e = 101, - SDLK_f = 102, - SDLK_g = 103, - SDLK_h = 104, - SDLK_i = 105, - SDLK_j = 106, - SDLK_k = 107, - SDLK_l = 108, - SDLK_m = 109, - SDLK_n = 110, - SDLK_o = 111, - SDLK_p = 112, - SDLK_q = 113, - SDLK_r = 114, - SDLK_s = 115, - SDLK_t = 116, - SDLK_u = 117, - SDLK_v = 118, - SDLK_w = 119, - SDLK_x = 120, - SDLK_y = 121, - SDLK_z = 122, - SDLK_DELETE = 127, - /* End of ASCII mapped keysyms */ - /*@}*/ - - /** @name International keyboard syms */ - /*@{*/ - SDLK_WORLD_0 = 160, /* 0xA0 */ - SDLK_WORLD_1 = 161, - SDLK_WORLD_2 = 162, - SDLK_WORLD_3 = 163, - SDLK_WORLD_4 = 164, - SDLK_WORLD_5 = 165, - SDLK_WORLD_6 = 166, - SDLK_WORLD_7 = 167, - SDLK_WORLD_8 = 168, - SDLK_WORLD_9 = 169, - SDLK_WORLD_10 = 170, - SDLK_WORLD_11 = 171, - SDLK_WORLD_12 = 172, - SDLK_WORLD_13 = 173, - SDLK_WORLD_14 = 174, - SDLK_WORLD_15 = 175, - SDLK_WORLD_16 = 176, - SDLK_WORLD_17 = 177, - SDLK_WORLD_18 = 178, - SDLK_WORLD_19 = 179, - SDLK_WORLD_20 = 180, - SDLK_WORLD_21 = 181, - SDLK_WORLD_22 = 182, - SDLK_WORLD_23 = 183, - SDLK_WORLD_24 = 184, - SDLK_WORLD_25 = 185, - SDLK_WORLD_26 = 186, - SDLK_WORLD_27 = 187, - SDLK_WORLD_28 = 188, - SDLK_WORLD_29 = 189, - SDLK_WORLD_30 = 190, - SDLK_WORLD_31 = 191, - SDLK_WORLD_32 = 192, - SDLK_WORLD_33 = 193, - SDLK_WORLD_34 = 194, - SDLK_WORLD_35 = 195, - SDLK_WORLD_36 = 196, - SDLK_WORLD_37 = 197, - SDLK_WORLD_38 = 198, - SDLK_WORLD_39 = 199, - SDLK_WORLD_40 = 200, - SDLK_WORLD_41 = 201, - SDLK_WORLD_42 = 202, - SDLK_WORLD_43 = 203, - SDLK_WORLD_44 = 204, - SDLK_WORLD_45 = 205, - SDLK_WORLD_46 = 206, - SDLK_WORLD_47 = 207, - SDLK_WORLD_48 = 208, - SDLK_WORLD_49 = 209, - SDLK_WORLD_50 = 210, - SDLK_WORLD_51 = 211, - SDLK_WORLD_52 = 212, - SDLK_WORLD_53 = 213, - SDLK_WORLD_54 = 214, - SDLK_WORLD_55 = 215, - SDLK_WORLD_56 = 216, - SDLK_WORLD_57 = 217, - SDLK_WORLD_58 = 218, - SDLK_WORLD_59 = 219, - SDLK_WORLD_60 = 220, - SDLK_WORLD_61 = 221, - SDLK_WORLD_62 = 222, - SDLK_WORLD_63 = 223, - SDLK_WORLD_64 = 224, - SDLK_WORLD_65 = 225, - SDLK_WORLD_66 = 226, - SDLK_WORLD_67 = 227, - SDLK_WORLD_68 = 228, - SDLK_WORLD_69 = 229, - SDLK_WORLD_70 = 230, - SDLK_WORLD_71 = 231, - SDLK_WORLD_72 = 232, - SDLK_WORLD_73 = 233, - SDLK_WORLD_74 = 234, - SDLK_WORLD_75 = 235, - SDLK_WORLD_76 = 236, - SDLK_WORLD_77 = 237, - SDLK_WORLD_78 = 238, - SDLK_WORLD_79 = 239, - SDLK_WORLD_80 = 240, - SDLK_WORLD_81 = 241, - SDLK_WORLD_82 = 242, - SDLK_WORLD_83 = 243, - SDLK_WORLD_84 = 244, - SDLK_WORLD_85 = 245, - SDLK_WORLD_86 = 246, - SDLK_WORLD_87 = 247, - SDLK_WORLD_88 = 248, - SDLK_WORLD_89 = 249, - SDLK_WORLD_90 = 250, - SDLK_WORLD_91 = 251, - SDLK_WORLD_92 = 252, - SDLK_WORLD_93 = 253, - SDLK_WORLD_94 = 254, - SDLK_WORLD_95 = 255, /* 0xFF */ - /*@}*/ - - /** @name Numeric keypad */ - /*@{*/ - SDLK_KP0 = 256, - SDLK_KP1 = 257, - SDLK_KP2 = 258, - SDLK_KP3 = 259, - SDLK_KP4 = 260, - SDLK_KP5 = 261, - SDLK_KP6 = 262, - SDLK_KP7 = 263, - SDLK_KP8 = 264, - SDLK_KP9 = 265, - SDLK_KP_PERIOD = 266, - SDLK_KP_DIVIDE = 267, - SDLK_KP_MULTIPLY = 268, - SDLK_KP_MINUS = 269, - SDLK_KP_PLUS = 270, - SDLK_KP_ENTER = 271, - SDLK_KP_EQUALS = 272, - /*@}*/ - - /** @name Arrows + Home/End pad */ - /*@{*/ - SDLK_UP = 273, - SDLK_DOWN = 274, - SDLK_RIGHT = 275, - SDLK_LEFT = 276, - SDLK_INSERT = 277, - SDLK_HOME = 278, - SDLK_END = 279, - SDLK_PAGEUP = 280, - SDLK_PAGEDOWN = 281, - /*@}*/ - - /** @name Function keys */ - /*@{*/ - SDLK_F1 = 282, - SDLK_F2 = 283, - SDLK_F3 = 284, - SDLK_F4 = 285, - SDLK_F5 = 286, - SDLK_F6 = 287, - SDLK_F7 = 288, - SDLK_F8 = 289, - SDLK_F9 = 290, - SDLK_F10 = 291, - SDLK_F11 = 292, - SDLK_F12 = 293, - SDLK_F13 = 294, - SDLK_F14 = 295, - SDLK_F15 = 296, - /*@}*/ - - /** @name Key state modifier keys */ - /*@{*/ - SDLK_NUMLOCK = 300, - SDLK_CAPSLOCK = 301, - SDLK_SCROLLOCK = 302, - SDLK_RSHIFT = 303, - SDLK_LSHIFT = 304, - SDLK_RCTRL = 305, - SDLK_LCTRL = 306, - SDLK_RALT = 307, - SDLK_LALT = 308, - SDLK_RMETA = 309, - SDLK_LMETA = 310, - SDLK_LSUPER = 311, /**< Left "Windows" key */ - SDLK_RSUPER = 312, /**< Right "Windows" key */ - SDLK_MODE = 313, /**< "Alt Gr" key */ - SDLK_COMPOSE = 314, /**< Multi-key compose key */ - /*@}*/ - - /** @name Miscellaneous function keys */ - /*@{*/ - SDLK_HELP = 315, - SDLK_PRINT = 316, - SDLK_SYSREQ = 317, - SDLK_BREAK = 318, - SDLK_MENU = 319, - SDLK_POWER = 320, /**< Power Macintosh power key */ - SDLK_EURO = 321, /**< Some european keyboards */ - SDLK_UNDO = 322, /**< Atari keyboard has Undo */ - /*@}*/ - - /* Add any other keys here */ - - SDLK_LAST -} SDLKey; - -/** Enumeration of valid key mods (possibly OR'd together) */ -typedef enum { - KMOD_NONE = 0x0000, - KMOD_LSHIFT= 0x0001, - KMOD_RSHIFT= 0x0002, - KMOD_LCTRL = 0x0040, - KMOD_RCTRL = 0x0080, - KMOD_LALT = 0x0100, - KMOD_RALT = 0x0200, - KMOD_LMETA = 0x0400, - KMOD_RMETA = 0x0800, - KMOD_NUM = 0x1000, - KMOD_CAPS = 0x2000, - KMOD_MODE = 0x4000, - KMOD_RESERVED = 0x8000 -} SDLMod; - -#define KMOD_CTRL (KMOD_LCTRL|KMOD_RCTRL) -#define KMOD_SHIFT (KMOD_LSHIFT|KMOD_RSHIFT) -#define KMOD_ALT (KMOD_LALT|KMOD_RALT) -#define KMOD_META (KMOD_LMETA|KMOD_RMETA) - -#endif /* _SDL_keysym_h */ diff --git a/src/gui/interface/Label.cpp b/src/gui/interface/Label.cpp index 310eb77e1..b7d767f14 100644 --- a/src/gui/interface/Label.cpp +++ b/src/gui/interface/Label.cpp @@ -1,14 +1,11 @@ #include "Label.h" - #include "Format.h" #include "Point.h" -#include "Keys.h" -#include "Mouse.h" -#include "PowderToy.h" +#include "PowderToySDL.h" #include "ContextMenu.h" - #include "graphics/Graphics.h" #include "graphics/FontReader.h" +#include using namespace ui; @@ -251,48 +248,47 @@ void Label::Draw(const Point& screenPos) { if (selectionLineH == selectionLineL) { - g->fillrect( - screenPos.X + textPosition.X + selectionXL - 1, - screenPos.Y + textPosition.Y + selectionYL - 2, - selectionXH - selectionXL + 1, - FONT_H, - 255, 255, 255, 255 + g->DrawFilledRect( + RectSized( + screenPos + textPosition + Vec2{ selectionXL - 1, selectionYL - 2 }, + Vec2{ selectionXH - selectionXL + 1, FONT_H } + ), + 0xFFFFFF_rgb ); } else { - g->fillrect( - screenPos.X + textPosition.X + selectionXL - 1, - screenPos.Y + textPosition.Y + selectionYL - 2, - textSize.X - selectionXL + 1, - FONT_H, - 255, 255, 255, 255 + g->DrawFilledRect( + RectSized( + screenPos + textPosition + Vec2{ selectionXL - 1, selectionYL - 2 }, + Vec2{ textSize.X - selectionXL + 1, FONT_H } + ), + 0xFFFFFF_rgb ); for (int i = 1; i < selectionLineH - selectionLineL; ++i) { - g->fillrect( - screenPos.X + textPosition.X - 1, - screenPos.Y + textPosition.Y + selectionYL - 2 + i * FONT_H, - textSize.X + 1, - FONT_H, - 255, 255, 255, 255 + g->DrawFilledRect( + RectSized( + screenPos + textPosition + Vec2{ -1, selectionYL - 2 + i * FONT_H }, + Vec2{ textSize.X + 1, FONT_H } + ), + 0xFFFFFF_rgb ); } - g->fillrect( - screenPos.X + textPosition.X - 1, - screenPos.Y + textPosition.Y + selectionYH - 2, - selectionXH + 1, - FONT_H, - 255, 255, 255, 255 + g->DrawFilledRect( + RectSized( + screenPos + textPosition + Vec2{ -1, selectionYH - 2 }, + Vec2{ selectionXH + 1, FONT_H } + ), + 0xFFFFFF_rgb ); } } - g->drawtext( - screenPos.X + textPosition.X, - screenPos.Y + textPosition.Y, + g->BlendText( + screenPos + textPosition, displayTextWithSelection, - textColour.Red, textColour.Green, textColour.Blue, 255 + textColour.NoAlpha().WithAlpha(255) ); } diff --git a/src/gui/interface/Label.h b/src/gui/interface/Label.h index 37433b540..17891bcd5 100644 --- a/src/gui/interface/Label.h +++ b/src/gui/interface/Label.h @@ -1,6 +1,4 @@ -#ifndef LABEL_H -#define LABEL_H - +#pragma once #include "common/String.h" #include "Component.h" @@ -67,5 +65,3 @@ namespace ui void Tick(float dt) override; }; } - -#endif // LABEL_H diff --git a/src/gui/interface/Mouse.h b/src/gui/interface/Mouse.h deleted file mode 100644 index 5095ce15a..000000000 --- a/src/gui/interface/Mouse.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/src/gui/interface/Panel.cpp b/src/gui/interface/Panel.cpp index 4eb0828bf..8bfdc395b 100644 --- a/src/gui/interface/Panel.cpp +++ b/src/gui/interface/Panel.cpp @@ -67,31 +67,23 @@ void Panel::Draw(const Point& screenPos) // draw ourself first XDraw(screenPos); - int x = screenPos.X; - int y = screenPos.Y; - int w = Size.X; - int h = Size.Y; - ui::Engine::Ref().g->SetClipRect(x, y, w, h); // old cliprect is now in x, y, w, h + auto clip = RectSized(screenPos, Size); + GetGraphics()->SwapClipRect(clip); // attempt to draw all children - for (size_t i = 0; i < children.size(); ++i) - { + for (auto const child : children) // the component must be visible - if (children[i]->Visible) + if (child->Visible) { + auto rect = RectSized(child->Position + ViewportPosition, child->Size); //check if the component is in the screen, draw if it is - if (children[i]->Position.X + ViewportPosition.X + children[i]->Size.X >= 0 && - children[i]->Position.Y + ViewportPosition.Y + children[i]->Size.Y >= 0 && - children[i]->Position.X + ViewportPosition.X < ui::Engine::Ref().GetWidth() && - children[i]->Position.Y + ViewportPosition.Y < ui::Engine::Ref().GetHeight() ) + if (rect & Size.OriginRect()) { - Point scrpos = screenPos + children[i]->Position + ViewportPosition; - children[i]->Draw(scrpos); + child->Draw(screenPos + rect.TopLeft); } } - } - ui::Engine::Ref().g->SetClipRect(x, y, w, h); // apply old cliprect + GetGraphics()->SwapClipRect(clip); // apply old cliprect } void Panel::Tick(float dt) diff --git a/src/gui/interface/Panel.h b/src/gui/interface/Panel.h index 8ea827f0e..f6a98e617 100644 --- a/src/gui/interface/Panel.h +++ b/src/gui/interface/Panel.h @@ -1,9 +1,8 @@ #pragma once -#include - #include "graphics/Pixel.h" #include "gui/interface/Point.h" #include "gui/interface/Component.h" +#include class Graphics; namespace ui @@ -16,7 +15,8 @@ namespace ui * See sys::Component */ -class Component; + class Component; + class Panel : public Component { public: diff --git a/src/gui/interface/Point.h b/src/gui/interface/Point.h index 0976162d2..7a64e5e69 100644 --- a/src/gui/interface/Point.h +++ b/src/gui/interface/Point.h @@ -1,146 +1,8 @@ #pragma once +#include "common/Vec2.h" + namespace ui { - -//Lightweight 2D Int32/Float32 Point struct for UI -struct Point -{ -#if ENABLE_FLOAT_UI -# define POINT_T float -#else -# define POINT_T int -#endif - - POINT_T X; - POINT_T Y; - - Point(POINT_T x, POINT_T y) - : X(x) - , Y(y) - { - } - - inline Point operator - () const - { - return Point(-X, -Y); - } - - inline Point operator + (const Point& v) const - { - return Point(X + v.X, Y + v.Y); - } - - inline Point operator + (const int v) const - { - return Point(X + v, Y + v); - } - - inline Point operator - (const Point& v) const - { - return Point(X - v.X, Y - v.Y); - } - - inline Point operator - (const int v) const - { - return Point(X - v, Y - v); - } - - inline Point operator * (const Point& v) const - { - return Point(X * v.X, Y * v.Y); - } - - inline Point operator * (int v) const - { - return Point(X * static_cast(v), Y * static_cast(v)); - } - - inline Point operator * (float v) const - { - return Point(X * static_cast(v), Y * static_cast(v)); - } - - inline Point operator / (const Point& v) const - { - return Point(X / v.X, Y / v.Y); - } - - inline Point operator / (int v) const - { - return Point(X / static_cast(v), Y / static_cast(v)); - } - - inline Point operator / (float v) const - { - return Point(X / static_cast(v), Y / static_cast(v)); - } - - inline void operator += (const Point& v) - { - X += v.X; - Y += v.Y; - } - - inline void operator -= (const Point& v) - { - X -= v.X; - Y -= v.Y; - } - - inline void operator *= (const Point& v) - { - X *= v.X; - Y *= v.Y; - } - - inline void operator *= (int v) - { - X *= static_cast(v); - Y *= static_cast(v); - } - - inline void operator *= (float v) - { - X *= static_cast(v); - Y *= static_cast(v); - } - - inline void operator /= (const Point& v) - { - X /= v.X; - Y /= v.Y; - } - - inline void operator /= (int v) - { - X /= static_cast(v); - Y /= static_cast(v); - } - - inline void operator /= (float v) - { - X /= static_cast(v); - Y /= static_cast(v); - } - - inline bool operator == (const Point& v) const - { - return (X == v.X && Y == v.Y); - } - - inline bool operator != (const Point& v) const - { - return (X != v.X || Y != v.Y); - } - - inline Point operator = (const Point& v) - { - X = v.X; - Y = v.Y; - return Point(X, Y); - } - -}; - + using Point = Vec2; } diff --git a/src/gui/interface/ProgressBar.cpp b/src/gui/interface/ProgressBar.cpp index 45617a39c..c5807fc0f 100644 --- a/src/gui/interface/ProgressBar.cpp +++ b/src/gui/interface/ProgressBar.cpp @@ -40,42 +40,37 @@ String ProgressBar::GetStatus() return progressStatus; } -void ProgressBar::Draw(const Point & screenPos) +void ProgressBar::Draw(const Point &screenPos) { - Graphics * g = GetGraphics(); - - ui::Colour progressBarColour = style::Colour::WarningTitle; - - g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255); - - if(progress!=-1) + Graphics *g = GetGraphics(); + g->DrawRect(RectSized(screenPos, Size), 0xFFFFFF_rgb); + auto inner = RectSized(screenPos + Vec2{ 2, 2 }, Size - Vec2{ 4, 4 }); + auto drawContent = [this, screenPos, g, inner](int beginX, int endX, ui::Colour bgColour, ui::Colour textColour) { + auto clip = RectSized(inner.TopLeft + Vec2{ beginX, 0 }, Vec2{ endX - beginX, inner.Size().Y }) & g->GetClipRect(); + g->SwapClipRect(clip); + if (bgColour.Alpha) + { + g->DrawFilledRect(inner, bgColour.NoAlpha()); + } + g->BlendText(screenPos + Vec2{ + (Size.X - (Graphics::TextSize(progressStatus).X - 1)) / 2, + (Size.Y - 8) / 2 + }, progressStatus, textColour); + g->SwapClipRect(clip); + }; + drawContent(0, inner.Size().X, 0x000000_rgb .WithAlpha(0), 0xFFFFFF_rgb .WithAlpha(255)); + if (progress == -1) { - if(progress > 0) - { - if(progress > 100) - progress = 100; - float size = float(Size.X-4)*(float(progress)/100.0f); // TIL... - size = std::min(std::max(size, 0.0f), float(Size.X-4)); - g->fillrect(screenPos.X + 2, screenPos.Y + 2, int(size), Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); - } - } else { - int size = 40, rsize = 0; - float position = float(Size.X-4)*(intermediatePos/100.0f); - if(position + size - 1 > Size.X-4) - { - size = int((Size.X-4)-position+1); - rsize = 40-size; - } - g->fillrect(screenPos.X + 2 + int(position), screenPos.Y + 2, size, Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); - if(rsize) - { - g->fillrect(screenPos.X + 2, screenPos.Y + 2, rsize, Size.Y-4, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); - } + constexpr auto size = 40; + auto pos = int(inner.Size().X * intermediatePos / 100); + drawContent(pos, pos + size, style::Colour::WarningTitle, 0x000000_rgb .WithAlpha(255)); + pos -= inner.Size().X; + drawContent(pos, pos + size, style::Colour::WarningTitle, 0x000000_rgb .WithAlpha(255)); } - if(progress<50) - g->drawtext(screenPos.X + ((Size.X-Graphics::textwidth(progressStatus))/2), screenPos.Y + (Size.Y-8)/2, progressStatus, 255, 255, 255, 255); else - g->drawtext(screenPos.X + ((Size.X-Graphics::textwidth(progressStatus))/2), screenPos.Y + (Size.Y-8)/2, progressStatus, 0, 0, 0, 255); + { + drawContent(0, inner.Size().X * progress / 100, style::Colour::WarningTitle, 0x000000_rgb .WithAlpha(255)); + } } void ProgressBar::Tick(float dt) diff --git a/src/gui/interface/RichLabel.cpp b/src/gui/interface/RichLabel.cpp index 9d933a983..efa6a7d5f 100644 --- a/src/gui/interface/RichLabel.cpp +++ b/src/gui/interface/RichLabel.cpp @@ -1,16 +1,12 @@ #include "RichLabel.h" - -#include -#include - #include "Colour.h" - -#include "common/Platform.h" +#include "common/platform/Platform.h" #include "graphics/FontReader.h" #include "graphics/Graphics.h" - #include "gui/interface/Component.h" #include "gui/interface/Point.h" +#include +#include using namespace ui; @@ -183,7 +179,7 @@ void RichLabel::Draw(const Point& screenPos) { Graphics * g = GetGraphics(); ui::Colour textColour = Appearance.TextInactive; - g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, displayText, textColour.Red, textColour.Green, textColour.Blue, 255); + g->BlendText(screenPos + textPosition, displayText, textColour.NoAlpha().WithAlpha(255)); } void RichLabel::OnMouseClick(int x, int y, unsigned button) diff --git a/src/gui/interface/SaveButton.cpp b/src/gui/interface/SaveButton.cpp index 1b82b8ce2..66b14ea75 100644 --- a/src/gui/interface/SaveButton.cpp +++ b/src/gui/interface/SaveButton.cpp @@ -2,8 +2,6 @@ #include "ContextMenu.h" #include "Format.h" -#include "Keys.h" -#include "Mouse.h" #include "client/Client.h" #include "client/ThumbnailRendererTask.h" @@ -13,12 +11,13 @@ #include "gui/dialogues/ErrorMessage.h" #include "graphics/Graphics.h" +#include "SimulationConfig.h" +#include + namespace ui { SaveButton::SaveButton(Point position, Point size) : Component(position, size), - file(nullptr), - save(nullptr), wantsDraw(false), triedThumbnail(false), isMouseInsideAuthor(false), @@ -32,16 +31,16 @@ SaveButton::SaveButton(Point position, Point size) : { } -SaveButton::SaveButton(Point position, Point size, SaveInfo * save_) : SaveButton(position, size) +SaveButton::SaveButton(Point position, Point size, SaveInfo *newSave /* non-owning */) : SaveButton(position, size) { - save = save_; + save = newSave; if(save) { name = save->name; - if(Graphics::textwidth(name) > Size.X) + if (Graphics::TextSize(name).X - 1 > Size.X) { - int position = Graphics::textwidthx(name, Size.X - 22); - name = name.erase(position, name.length()-position); + auto it = Graphics::TextFit(name, Size.X - (Appearance.icon ? 38 : 22)); + name.erase(it, name.end()); name += "..."; } @@ -93,16 +92,16 @@ SaveButton::SaveButton(Point position, Point size, SaveInfo * save_) : SaveButto } } -SaveButton::SaveButton(Point position, Point size, SaveFile * file_) : SaveButton(position, size) +SaveButton::SaveButton(Point position, Point size, SaveFile *newFile /* non-owning */) : SaveButton(position, size) { - file = file_; + file = newFile; if(file) { name = file->GetDisplayName(); - if(Graphics::textwidth(name) > Size.X) + if (Graphics::TextSize(name).X - 1 > Size.X) { - int position = Graphics::textwidthx(name, Size.X - 22); - name = name.erase(position, name.length()-position); + auto it = Graphics::TextFit(name, Size.X - (Appearance.icon ? 38 : 22)); + name.erase(it, name.end()); name += "..."; } } @@ -114,13 +113,6 @@ SaveButton::~SaveButton() { thumbnailRenderer->Abandon(); } - delete save; - delete file; -} - -void SaveButton::OnResponse(std::unique_ptr Thumbnail) -{ - thumbnail = std::move(Thumbnail); } void SaveButton::Tick(float dt) @@ -135,26 +127,30 @@ void SaveButton::Tick(float dt) { if(save->GetGameSave()) { - thumbnailRenderer = new ThumbnailRendererTask(save->GetGameSave(), thumbBoxSize.X, thumbBoxSize.Y); + thumbnailRenderer = new ThumbnailRendererTask(*save->GetGameSave(), thumbBoxSize, true, true); thumbnailRenderer->Start(); triedThumbnail = true; } else if (save->GetID()) { - RequestSetup(save->GetID(), save->GetVersion(), thumbBoxSize.X, thumbBoxSize.Y); - RequestStart(); + thumbnailRequest = std::make_unique(save->GetID(), save->GetVersion(), thumbBoxSize); + thumbnailRequest->Start(); triedThumbnail = true; } } else if (file && file->GetGameSave()) { - thumbnailRenderer = new ThumbnailRendererTask(file->GetGameSave(), thumbBoxSize.X, thumbBoxSize.Y, true, true, false); + thumbnailRenderer = new ThumbnailRendererTask(*file->GetGameSave(), thumbBoxSize, true, false); thumbnailRenderer->Start(); triedThumbnail = true; } } - RequestPoll(); + if (thumbnailRequest && thumbnailRequest->CheckDone()) + { + thumbnail = thumbnailRequest->Finish(); + thumbnailRequest.reset(); + } if (thumbnailRenderer) { @@ -168,7 +164,7 @@ void SaveButton::Tick(float dt) if (thumbnail && file) { - thumbSize = ui::Point(thumbnail->Width, thumbnail->Height); + thumbSize = thumbnail->Size(); } } if (file && !wantsDraw && !thumbnailRenderer) @@ -188,107 +184,74 @@ void SaveButton::Draw(const Point& screenPos) if(selected && selectable) { - g->fillrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 100, 170, 255, 100); + g->BlendFilledRect(RectSized(screenPos, Size), 0x64AAFF_rgb .WithAlpha(100)); } if (thumbnail) { //thumbBoxSize = ui::Point(thumbnail->Width, thumbnail->Height); - if (save && save->id) - g->draw_image(thumbnail.get(), screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 255); - else - g->draw_image(thumbnail.get(), screenPos.X+(Size.X-thumbSize.X)/2, screenPos.Y+(Size.Y-21-thumbSize.Y)/2, 255); + auto *tex = thumbnail.get(); + auto space = Size - Vec2{ 0, 21 }; + g->BlendImage(tex->Data(), 255, RectSized(screenPos + ((save && save->id) ? ((space - thumbBoxSize) / 2 - Vec2{ 3, 0 }) : (space - thumbSize) / 2), tex->Size())); } - else if (file && !file->GetGameSave()) - g->drawtext(screenPos.X+(Size.X-Graphics::textwidth("Error loading save"))/2, screenPos.Y+(Size.Y-28)/2, "Error loading save", 180, 180, 180, 255); + else if (file && !file->LazyGetGameSave()) + g->BlendText(screenPos + Vec2{ (Size.X-(Graphics::TextSize("Error loading save").X - 1))/2, (Size.Y-28)/2 }, "Error loading save", 0xB4B4B4_rgb .WithAlpha(255)); if(save) { if(save->id) { - if(isMouseInside) - { - g->drawrect(screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255); - g->drawrect(screenPos.X-4+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 7, thumbBoxSize.Y, 210, 230, 255, 255); - } - else - { - g->drawrect(screenPos.X-3+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255); - g->drawrect(screenPos.X-4+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, 7, thumbBoxSize.Y, 180, 180, 180, 255); - } + g->DrawRect(RectSized(screenPos + Vec2{ - 3, 0 } + (Size - thumbBoxSize - Vec2{ 0, 21 }) / 2, thumbBoxSize), isMouseInside ? 0xD2E6FF_rgb : 0xB4B4B4_rgb); + g->DrawRect(RectSized(screenPos + Vec2{ thumbBoxSize.X - 4, 0 } + (Size - thumbBoxSize - Vec2{ 0, 21 }) / 2, Vec2{ 7, thumbBoxSize.Y }), isMouseInside ? 0xD2E6FF_rgb : 0xB4B4B4_rgb); - g->fillrect(screenPos.X-3+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+1+(Size.Y-20-thumbBoxSize.Y)/2, 5, (thumbBoxSize.Y+1)/2-1, 0, 107, 10, 255); - g->fillrect(screenPos.X-3+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2, 5, thumbBoxSize.Y/2-1, 107, 10, 0, 255); + g->DrawFilledRect(RectSized(screenPos + Vec2{ -3+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, 1+(Size.Y-20-thumbBoxSize.Y)/2 }, Vec2{ 5, (thumbBoxSize.Y+1)/2-1 }), 0x006B0A_rgb); + g->DrawFilledRect(RectSized(screenPos + Vec2{ -3+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, (Size.Y-20)/2 }, Vec2{ 5, thumbBoxSize.Y/2-1 }), 0x6B0A00_rgb); - g->fillrect(screenPos.X-2+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2-voteBarHeightUp, 3, voteBarHeightUp, 57, 187, 57, 255); //green - g->fillrect(screenPos.X-2+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-20)/2, 3, voteBarHeightDown, 187, 57, 57, 255); //red + g->DrawFilledRect(RectSized(screenPos + Vec2{ -2+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, (Size.Y-20)/2-voteBarHeightUp }, Vec2{ 3, voteBarHeightUp }), 0x39BB39_rgb); //green + g->DrawFilledRect(RectSized(screenPos + Vec2{ -2+thumbBoxSize.X+(Size.X-thumbBoxSize.X)/2, (Size.Y-20)/2 }, Vec2{ 3, voteBarHeightDown }), 0xBB3939_rgb); //red } else { - if(isMouseInside) - g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255); - else - g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255); + g->DrawRect(RectSized(screenPos + (Size - thumbBoxSize - Vec2{ 0, 21 }) / 2, thumbBoxSize), isMouseInside ? 0xD2E6FF_rgb : 0xB4B4B4_rgb); } - if(isMouseInside && !isMouseInsideAuthor) - g->drawtext(screenPos.X+(Size.X-Graphics::textwidth(name))/2, screenPos.Y+Size.Y - 21, name, 255, 255, 255, 255); - else - g->drawtext(screenPos.X+(Size.X-Graphics::textwidth(name))/2, screenPos.Y+Size.Y - 21, name, 180, 180, 180, 255); - - if(isMouseInsideAuthor) - g->drawtext(screenPos.X+(Size.X-Graphics::textwidth(save->userName.FromUtf8()))/2, screenPos.Y+Size.Y - 10, save->userName.FromUtf8(), 200, 230, 255, 255); - else - g->drawtext(screenPos.X+(Size.X-Graphics::textwidth(save->userName.FromUtf8()))/2, screenPos.Y+Size.Y - 10, save->userName.FromUtf8(), 100, 130, 160, 255); + g->BlendText(screenPos + Vec2{ (Size.X-(Graphics::TextSize(name).X - 1))/2, Size.Y - 21 }, name, (isMouseInside && !isMouseInsideAuthor) ? 0xFFFFFF_rgb .WithAlpha(255) : 0xB4B4B4_rgb .WithAlpha(255)); + g->BlendText(screenPos + Vec2{ (Size.X-(Graphics::TextSize(save->userName.FromUtf8()).X - 1))/2, Size.Y - 10 }, save->userName.FromUtf8(), isMouseInsideAuthor ? 0xC8E6FF_rgb .WithAlpha(255) : 0x6482A0_rgb .WithAlpha(255)); if (showVotes)// && !isMouseInside) { - int x = screenPos.X-7+(Size.X-thumbBoxSize.X)/2+thumbBoxSize.X-Graphics::textwidth(votesBackground); + int x = screenPos.X-7+(Size.X-thumbBoxSize.X)/2+thumbBoxSize.X-(Graphics::TextSize(votesBackground).X - 1); int y = screenPos.Y-23+(Size.Y-thumbBoxSize.Y)/2+thumbBoxSize.Y; - g->drawtext(x, y, votesBackground, 16, 72, 16, 255); - g->drawtext(x, y, votesBackground2, 192, 192, 192, 255); - g->drawtext(x+3, y, votesString, 255, 255, 255, 255); + g->BlendText({ x, y }, votesBackground, 0x104810_rgb .WithAlpha(255)); + g->BlendText({ x, y }, votesBackground2, 0xC0C0C0_rgb .WithAlpha(255)); + g->BlendText({ x+3, y }, votesString, 0xFFFFFF_rgb .WithAlpha(255)); } if (isMouseInsideHistory && showVotes) { int x = screenPos.X; int y = screenPos.Y-15+(Size.Y-thumbBoxSize.Y)/2+thumbBoxSize.Y; - g->fillrect(x+1, y+1, 7, 8, 255, 255, 255, 255); - if (isMouseInsideHistory) { - g->drawtext(x, y, 0xE026, 200, 100, 80, 255); - } else { - g->drawtext(x, y, 0xE026, 160, 70, 50, 255); - } + g->DrawFilledRect(RectSized(Vec2{ x+1, y+1 }, Vec2{ 7, 8 }), 0xFFFFFF_rgb); + g->BlendText({ x, y }, 0xE026, isMouseInsideHistory ? 0xC86450_rgb .WithAlpha(255) : 0xA04632_rgb .WithAlpha(255)); } if (!save->GetPublished()) { - g->drawtext(screenPos.X, screenPos.Y-2, 0xE04D, 255, 255, 255, 255); - g->drawtext(screenPos.X, screenPos.Y-2, 0xE04E, 212, 151, 81, 255); + g->BlendText(screenPos - Vec2{ 0, 2 }, 0xE04D, 0xFFFFFF_rgb .WithAlpha(255)); + g->BlendText(screenPos - Vec2{ 0, 2 }, 0xE04E, 0xD49751_rgb .WithAlpha(255)); } } else if (file) { - if (isMouseInside) - g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 210, 230, 255, 255); - else - g->drawrect(screenPos.X+(Size.X-thumbBoxSize.X)/2, screenPos.Y+(Size.Y-21-thumbBoxSize.Y)/2, thumbBoxSize.X, thumbBoxSize.Y, 180, 180, 180, 255); + g->DrawRect(RectSized(screenPos + (Size - thumbBoxSize- Vec2{ 0, 21 }) / 2, thumbBoxSize), isMouseInside ? 0xD2E6FF_rgb : 0xB4B4B4_rgb); if (thumbSize.X) - g->xor_rect(screenPos.X+(Size.X-thumbSize.X)/2, screenPos.Y+(Size.Y-21-thumbSize.Y)/2, thumbSize.X, thumbSize.Y); + g->XorDottedRect(RectSized(screenPos + (Size - thumbSize - Vec2{ 0, 21 }) / 2, thumbSize)); - if (isMouseInside) - { - g->drawtext(screenPos.X+(Size.X-Graphics::textwidth(name))/2, screenPos.Y+Size.Y - 21, name, 255, 255, 255, 255); - } - else - { - g->drawtext(screenPos.X+(Size.X-Graphics::textwidth(name))/2, screenPos.Y+Size.Y - 21, name, 180, 180, 180, 255); - } + g->BlendText(screenPos + Vec2{ (Size.X-(Graphics::TextSize(name).X - 1))/2, Size.Y - 21 }, name, isMouseInside ? 0xFFFFFF_rgb .WithAlpha(255) : 0xB4B4B4_rgb .WithAlpha(255)); } if(isMouseInside && selectable) { - g->clearrect(screenPos.X+(Size.X-20), screenPos.Y+6, 14, 14); - g->drawrect(screenPos.X+(Size.X-20), screenPos.Y+6, 14, 14, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(screenPos + Vec2{ Size.X - 19, 7 }, Vec2{ 13, 13 }), 0x000000_rgb); + g->DrawRect(RectSized(screenPos + Vec2{ Size.X-20, 6 }, Vec2{ 14, 14 }), 0xFFFFFF_rgb); if(selected) - g->fillrect(screenPos.X+(Size.X-18), screenPos.Y+8, 10, 10, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(screenPos + Vec2{ Size.X-18, 8 }, Vec2{ 10, 10 }), 0xFFFFFF_rgb); } } @@ -438,4 +401,13 @@ void SaveButton::DoSelection() actionCallback.selected(); } +std::unique_ptr SaveButton::CloneThumbnail() const +{ + if (thumbnail) + { + return std::make_unique(*thumbnail); + } + return nullptr; +} + } /* namespace ui */ diff --git a/src/gui/interface/SaveButton.h b/src/gui/interface/SaveButton.h index 553082b8e..ead5504f2 100644 --- a/src/gui/interface/SaveButton.h +++ b/src/gui/interface/SaveButton.h @@ -1,11 +1,8 @@ -#ifndef SAVEBUTTON_H_ -#define SAVEBUTTON_H_ - +#pragma once #include "common/String.h" #include "Component.h" #include "client/http/ThumbnailRequest.h" -#include "client/http/RequestMonitor.h" #include #include @@ -16,10 +13,10 @@ class SaveInfo; class ThumbnailRendererTask; namespace ui { -class SaveButton : public Component, public http::RequestMonitor +class SaveButton : public Component { - SaveFile * file; - SaveInfo * save; + SaveFile *file = nullptr; // non-owning + SaveInfo *save = nullptr; // non-owning std::unique_ptr thumbnail; ui::Point thumbSize = ui::Point(0, 0); String name; @@ -35,6 +32,8 @@ class SaveButton : public Component, public http::RequestMonitor thumbnailRequest; + struct SaveButtonAction { std::function action, altAction, altAltAction, selected; @@ -44,8 +43,8 @@ class SaveButton : public Component, public http::RequestMonitor thumbnail) override; - void SetSelected(bool selected_) { selected = selected_; } bool GetSelected() { return selected; } void SetSelectable(bool selectable_) { selectable = selectable_; } bool GetSelectable() { return selectable; } void SetShowVotes(bool showVotes_) { showVotes = showVotes_; } - SaveInfo * GetSave() { return save; } - SaveFile * GetSaveFile() { return file; } + const SaveInfo *GetSave() const { return save; } + const SaveFile *GetSaveFile() const { return file; } inline bool GetState() { return state; } void DoAction(); void DoAltAction(); void DoAltAction2(); void DoSelection(); inline void SetActionCallback(SaveButtonAction action) { actionCallback = action; } + + // TODO: clone the request instead because sometimes the user of CloneThumbnail might end up + // with a nullptr even though the thumbnail for the SaveButton will eventually arrive. + std::unique_ptr CloneThumbnail() const; + protected: bool isButtonDown, state, isMouseInside, selected, selectable; }; } -#endif /* BUTTON_H_ */ - diff --git a/src/gui/interface/ScrollPanel.cpp b/src/gui/interface/ScrollPanel.cpp index b190c23d0..9814d44a8 100644 --- a/src/gui/interface/ScrollPanel.cpp +++ b/src/gui/interface/ScrollPanel.cpp @@ -65,8 +65,8 @@ void ScrollPanel::Draw(const Point& screenPos) scrollPos = float(Size.Y-scrollHeight)*(float(offsetY)/float(maxOffset.Y)); } - g->fillrect(screenPos.X+(Size.X-scrollBarWidth), screenPos.Y, scrollBarWidth, Size.Y, 125, 125, 125, 100); - g->fillrect(screenPos.X+(Size.X-scrollBarWidth), screenPos.Y+int(scrollPos), scrollBarWidth, int(scrollHeight)+1, 255, 255, 255, 255); + g->BlendFilledRect(RectSized(screenPos + Vec2{ Size.X - scrollBarWidth, 0 }, { scrollBarWidth, Size.Y }), 0x7D7D7D_rgb .WithAlpha(100)); + g->DrawFilledRect(RectSized(screenPos + Vec2{ Size.X - scrollBarWidth, int(scrollPos) }, { scrollBarWidth, int(scrollHeight)+1 }), 0xFFFFFF_rgb); } } diff --git a/src/gui/interface/Slider.cpp b/src/gui/interface/Slider.cpp index 791244669..eac1c9ad8 100644 --- a/src/gui/interface/Slider.cpp +++ b/src/gui/interface/Slider.cpp @@ -68,8 +68,8 @@ void Slider::SetColour(Colour col1, Colour col2) this->col1 = col1; this->col2 = col2; bgGradient = Graphics::Gradient({ - { pixel(PIXRGB(col1.Red, col1.Green, col1.Blue)), 0.f }, - { pixel(PIXRGB(col2.Red, col2.Green, col2.Blue)), 1.f }, + { col1.NoAlpha(), 0.f }, + { col2.NoAlpha(), 1.f }, }, Size.X-7); } @@ -104,7 +104,6 @@ void Slider::SetSteps(int steps) void Slider::Draw(const Point& screenPos) { Graphics * g = GetGraphics(); - //g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255); if (bgGradient.size()) { @@ -112,13 +111,12 @@ void Slider::Draw(const Point& screenPos) { for (int i = 3; i < Size.X-7; i++) { - auto color = bgGradient[i - 3]; - g->blendpixel(screenPos.X+i+2, screenPos.Y+j+2, PIXR(color), PIXG(color), PIXB(color), 255); + g->DrawPixel(screenPos + Vec2{ i + 2, j + 2 }, bgGradient[i - 3]); } } } - g->drawrect(screenPos.X+3, screenPos.Y+3, Size.X-6, Size.Y-6, 255, 255, 255, 255); + g->DrawRect(RectSized(screenPos + Vec2{ 3, 3 }, Size - Vec2{ 6, 6 }), 0xFFFFFF_rgb); auto fPosition = float(sliderPosition); auto fSize = float(Size.X-6); @@ -128,8 +126,8 @@ void Slider::Draw(const Point& screenPos) auto sliderX = int(fSliderX); sliderX += 3; - g->fillrect(screenPos.X+sliderX-2, screenPos.Y+1, 4, Size.Y-2, 20, 20, 20, 255); - g->drawrect(screenPos.X+sliderX-2, screenPos.Y+1, 4, Size.Y-2, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(screenPos + Vec2{ sliderX-2, 1 }, Vec2{ 4, Size.Y-2 }), 0x141414_rgb); + g->DrawRect(RectSized(screenPos + Vec2{ sliderX-2, 1 }, Vec2{ 4, Size.Y-2 }), 0xC8C8C8_rgb); } } /* namespace ui */ diff --git a/src/gui/interface/Slider.h b/src/gui/interface/Slider.h index bd8b20338..25bee8b3d 100644 --- a/src/gui/interface/Slider.h +++ b/src/gui/interface/Slider.h @@ -1,10 +1,7 @@ -#ifndef SLIDER_H_ -#define SLIDER_H_ - +#pragma once #include "Component.h" #include "Colour.h" #include "graphics/Pixel.h" - #include namespace ui { @@ -13,7 +10,7 @@ class Slider : public ui::Component int sliderSteps; int sliderPosition; bool isMouseDown; - std::vector bgGradient; + std::vector> bgGradient; struct SliderAction { @@ -40,4 +37,3 @@ public: }; } /* namespace ui */ -#endif /* SLIDER_H_ */ diff --git a/src/gui/interface/Spinner.cpp b/src/gui/interface/Spinner.cpp index 272b39488..3a145f333 100644 --- a/src/gui/interface/Spinner.cpp +++ b/src/gui/interface/Spinner.cpp @@ -1,8 +1,6 @@ #include "Spinner.h" - -#include - #include "graphics/Graphics.h" +#include using namespace ui; @@ -29,8 +27,10 @@ void Spinner::Draw(const Point& screenPos) int lineOuter = (Size.X/2)+3; for(float t = 0.0f; t < 6.0f; t+=0.25f) { - //g->drawblob(baseX+(sin(cValue+t)*(Size.X/2)), baseY+(cos(cValue+t)*(Size.X/2)), t*255, t*255, t*255); - g->draw_line(int(baseX+(sin(cValue+t)*lineInner)), int(baseY+(cos(cValue+t)*lineInner)), int(baseX+(sin(cValue+t)*lineOuter)), int(baseY+(cos(cValue+t)*lineOuter)), int((t/6)*255), int((t/6)*255), int((t/6)*255), 255); + g->DrawLine( + { int(baseX+(sin(cValue+t)*lineInner)), int(baseY+(cos(cValue+t)*lineInner)) }, + { int(baseX+(sin(cValue+t)*lineOuter)), int(baseY+(cos(cValue+t)*lineOuter)) }, + RGB(int((t/6)*255), int((t/6)*255), int((t/6)*255))); } } Spinner::~Spinner() diff --git a/src/gui/interface/Spinner.h b/src/gui/interface/Spinner.h index 693ae65e1..9629ab149 100644 --- a/src/gui/interface/Spinner.h +++ b/src/gui/interface/Spinner.h @@ -1,6 +1,4 @@ -#ifndef SPINNER_H_ -#define SPINNER_H_ - +#pragma once #include "Component.h" namespace ui @@ -19,5 +17,3 @@ public: } - -#endif /* SPINNER_H_ */ diff --git a/src/gui/interface/Textbox.cpp b/src/gui/interface/Textbox.cpp index 25a6825ea..1b44f2cc1 100644 --- a/src/gui/interface/Textbox.cpp +++ b/src/gui/interface/Textbox.cpp @@ -1,19 +1,13 @@ #include "Textbox.h" - -#include "Config.h" #include "Format.h" -#include "PowderToy.h" - -#include "common/Platform.h" +#include "PowderToySDL.h" +#include "common/platform/Platform.h" #include "graphics/FontReader.h" #include "graphics/Graphics.h" - #include "gui/interface/Engine.h" -#include "gui/interface/Keys.h" -#include "gui/interface/Mouse.h" #include "gui/interface/Point.h" - #include "ContextMenu.h" +#include using namespace ui; @@ -206,9 +200,9 @@ void Textbox::pasteIntoSelection() { newText = newText.Substr(0, limit-backingText.length()); } - if (!multiline && Graphics::textwidth(backingText + newText) > regionWidth) + if (!multiline && Graphics::TextSize(backingText + newText).X - 1 > regionWidth) { - int pLimit = regionWidth - Graphics::textwidth(backingText); + int pLimit = regionWidth - (Graphics::TextSize(backingText).X - 1); int pWidth = 0; auto it = newText.begin(); while (it != newText.end()) @@ -366,10 +360,11 @@ void Textbox::OnVKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, StopTextEditing(); if (HasSelection()) { - if (getLowerSelectionBound() < 0 || getHigherSelectionBound() > (int)backingText.length()) + int lowerBound = getLowerSelectionBound(), higherBound = getHigherSelectionBound(); + if (lowerBound < 0 || higherBound > (int)backingText.length()) return; - backingText.Erase(getLowerSelectionBound(), getHigherSelectionBound()); - cursor = getLowerSelectionBound(); + backingText.Erase(lowerBound, higherBound - lowerBound); + cursor = lowerBound; changed = true; } else if (backingText.length() && cursor < (int)backingText.length()) @@ -393,10 +388,11 @@ void Textbox::OnVKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, StopTextEditing(); if (HasSelection()) { - if (getLowerSelectionBound() < 0 || getHigherSelectionBound() > (int)backingText.length()) + int lowerBound = getLowerSelectionBound(), higherBound = getHigherSelectionBound(); + if (lowerBound < 0 || higherBound > (int)backingText.length()) return; - backingText.erase(backingText.begin()+getLowerSelectionBound(), backingText.begin()+getHigherSelectionBound()); - cursor = getLowerSelectionBound(); + backingText.Erase(lowerBound, higherBound - lowerBound); + cursor = lowerBound; changed = true; } else if (backingText.length() && cursor > 0) @@ -492,7 +488,7 @@ void Textbox::InsertText(String text) regionWidth -= 13; regionWidth -= Appearance.Margin.Left; regionWidth -= Appearance.Margin.Right; - if ((limit==String::npos || backingText.length() < limit) && (Graphics::textwidth(backingText + text) <= regionWidth || multiline)) + if ((limit==String::npos || backingText.length() < limit) && (Graphics::TextSize(backingText + text).X - 1 <= regionWidth || multiline)) { if (cursor == (int)backingText.length()) { @@ -617,16 +613,21 @@ void Textbox::Draw(const Point& screenPos) Graphics * g = GetGraphics(); if(IsFocused()) { - if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 255); - g->draw_line(screenPos.X+textPosition.X+cursorPositionX, screenPos.Y-2+textPosition.Y+cursorPositionY, screenPos.X+textPosition.X+cursorPositionX, screenPos.Y+9+textPosition.Y+cursorPositionY, 255, 255, 255, 255); + if(border) + g->DrawRect(RectSized(screenPos, Size), 0xFFFFFF_rgb); + g->DrawLine( + screenPos + textPosition + Vec2{ cursorPositionX, cursorPositionY-2 }, + screenPos + textPosition + Vec2{ cursorPositionX, cursorPositionY+9 }, + 0xFFFFFF_rgb); } else { if(!text.length()) { - g->drawtext(screenPos.X+textPosition.X, screenPos.Y+textPosition.Y, placeHolder, textColour.Red, textColour.Green, textColour.Blue, 170); + g->BlendText(screenPos + textPosition, placeHolder, textColour.NoAlpha().WithAlpha(170)); } - if(border) g->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 160, 160, 160, 255); + if(border) + g->DrawRect(RectSized(screenPos, Size), 0xA0A0A0_rgb); } if(Appearance.icon) g->draw_icon(screenPos.X+iconPosition.X, screenPos.Y+iconPosition.Y, Appearance.icon); diff --git a/src/gui/interface/Textbox.h b/src/gui/interface/Textbox.h index ee273af38..6db21e414 100644 --- a/src/gui/interface/Textbox.h +++ b/src/gui/interface/Textbox.h @@ -1,6 +1,4 @@ -#ifndef TEXTBOX_H -#define TEXTBOX_H - +#pragma once #include "Label.h" #include @@ -101,5 +99,3 @@ protected: } - -#endif // TEXTBOX_H diff --git a/src/gui/interface/Window.cpp b/src/gui/interface/Window.cpp index 4a1b7a983..d63ccf0d5 100644 --- a/src/gui/interface/Window.cpp +++ b/src/gui/interface/Window.cpp @@ -1,12 +1,15 @@ #include "Window.h" #include "Engine.h" -#include "Keys.h" #include "Component.h" #include "gui/interface/Button.h" #include "graphics/Graphics.h" +#include "Config.h" +#include "SimulationConfig.h" +#include + using namespace ui; Window::Window(Point _position, Point _size): @@ -18,9 +21,7 @@ Window::Window(Point _position, Point _size): cancelButton(NULL), focusedComponent_(NULL), hoverComponent(NULL), -#ifdef DEBUG debugMode(false), -#endif halt(false), destruct(false), stop(false) @@ -178,97 +179,59 @@ void Window::DoFileDrop(ByteString filename) void Window::DoDraw() { OnDraw(); - for (int i = 0, sz = Components.size(); i < sz; ++i) - if (Components[i]->Visible && ((Components[i] != focusedComponent_ && Components[i] != hoverComponent) || Components[i]->GetParent())) + auto drawChild = [this](Component *child) { + if (child->Visible) { - Point scrpos(Components[i]->Position.X + Position.X, Components[i]->Position.Y + Position.Y); - if (AllowExclusiveDrawing) - { - Components[i]->Draw(scrpos); - } - else - { - if (scrpos.X + Components[i]->Size.X >= 0 && - scrpos.Y + Components[i]->Size.Y >= 0 && - scrpos.X < ui::Engine::Ref().GetWidth() && - scrpos.Y < ui::Engine::Ref().GetHeight()) - { - Components[i]->Draw(scrpos); - } - } -#ifdef DEBUG + auto rect = RectSized(Position + child->Position, child->Size); + if (AllowExclusiveDrawing || bool(rect & GetGraphics()->Size().OriginRect())) + child->Draw(rect.TopLeft); + } + }; + for (auto child : Components) + if ((child != focusedComponent_ && child != hoverComponent) || child->GetParent()) + { + drawChild(child); + if (debugMode) - { - if (focusedComponent_==Components[i]) - { - ui::Engine::Ref().g->fillrect(Components[i]->Position.X+Position.X, Components[i]->Position.Y+Position.Y, Components[i]->Size.X, Components[i]->Size.Y, 0, 255, 0, 90); - } - else - { - ui::Engine::Ref().g->fillrect(Components[i]->Position.X+Position.X, Components[i]->Position.Y+Position.Y, Components[i]->Size.X, Components[i]->Size.Y, 255, 0, 0, 90); - } - } -#endif + GetGraphics()->BlendFilledRect(RectSized(Position + child->Position, child->Size), + (focusedComponent_ == child ? 0x00FF00_rgb : 0xFF0000_rgb).WithAlpha(0x5A)); } // the component the mouse is hovering over and the focused component are always drawn last - if (hoverComponent && hoverComponent->Visible && hoverComponent->GetParent() == NULL) + if (hoverComponent && hoverComponent->GetParent() == NULL) + drawChild(hoverComponent); + if (focusedComponent_ && focusedComponent_ != hoverComponent && focusedComponent_->GetParent() == NULL) + drawChild(focusedComponent_); + if (debugMode && focusedComponent_) { - Point scrpos(hoverComponent->Position.X + Position.X, hoverComponent->Position.Y + Position.Y); - if ((scrpos.X + hoverComponent->Size.X >= 0 && - scrpos.Y + hoverComponent->Size.Y >= 0 && - scrpos.X < ui::Engine::Ref().GetWidth() && - scrpos.Y < ui::Engine::Ref().GetHeight() - ) || AllowExclusiveDrawing) - { - hoverComponent->Draw(scrpos); - } + Graphics *g = ui::Engine::Ref().g; + + auto invPos = Size - (focusedComponent_->Position + focusedComponent_->Size); + String posText = String::Build( + "Position: L ", focusedComponent_->Position.X, + ", R ", invPos.X, + ", T: ", focusedComponent_->Position.Y, + ", B: ", invPos.Y + ); + String sizeText = String::Build( + "Size: ", focusedComponent_->Size.X, + ", ", focusedComponent_->Size.Y + ); + + auto pos = focusedComponent_->Position + Position + Vec2(focusedComponent_->Size.X + 5, 0); + pos.X = std::min(pos.X, g->Size().X - (Graphics::TextSize(posText).X - 1) - 5); + pos.X = std::min(pos.X, g->Size().X - (Graphics::TextSize(sizeText).X - 1) - 5); + + g->BlendText(pos + Vec2(0, 1), posText, 0x000000_rgb .WithAlpha(0xC8)); + g->BlendText(pos + Vec2(0, 0), posText, 0xFFFFFF_rgb .WithAlpha(0xFF)); + g->BlendText(pos + Vec2(0, 13), sizeText, 0x000000_rgb .WithAlpha(0xC8)); + g->BlendText(pos + Vec2(0, 12), sizeText, 0xFFFFFF_rgb .WithAlpha(0xFF)); } - if (focusedComponent_ && focusedComponent_ != hoverComponent && focusedComponent_->Visible && focusedComponent_->GetParent() == NULL) - { - Point scrpos(focusedComponent_->Position.X + Position.X, focusedComponent_->Position.Y + Position.Y); - if ((scrpos.X + focusedComponent_->Size.X >= 0 && - scrpos.Y + focusedComponent_->Size.Y >= 0 && - scrpos.X < ui::Engine::Ref().GetWidth() && - scrpos.Y < ui::Engine::Ref().GetHeight() - ) || AllowExclusiveDrawing) - { - focusedComponent_->Draw(scrpos); - } - } -#ifdef DEBUG - if (debugMode) - { - if (focusedComponent_) - { - int xPos = focusedComponent_->Position.X+focusedComponent_->Size.X+5+Position.X; - Graphics * g = ui::Engine::Ref().g; - String tempString, tempString2; - - tempString = String::Build("Position: L ", focusedComponent_->Position.X, ", R ", Size.X-(focusedComponent_->Position.X+focusedComponent_->Size.X), ", T: ", focusedComponent_->Position.Y, ", B: ", Size.Y-(focusedComponent_->Position.Y+focusedComponent_->Size.Y)); - tempString2 = String::Build("Size: ", focusedComponent_->Size.X, ", ", focusedComponent_->Size.Y); - - if (Graphics::textwidth(tempString)+xPos > WINDOWW) - xPos = WINDOWW-(Graphics::textwidth(tempString)+5); - if (Graphics::textwidth(tempString2)+xPos > WINDOWW) - xPos = WINDOWW-(Graphics::textwidth(tempString2)+5); - - g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+1, tempString, 0, 0, 0, 200); - g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y, tempString, 255, 255, 255, 255); - g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+13, tempString2, 0, 0, 0, 200); - g->drawtext(xPos, focusedComponent_->Position.Y+Position.Y+12, tempString2, 255, 255, 255, 255); - } - return; - } -#endif - } void Window::DoTick(float dt) { -#ifdef DEBUG if (debugMode) return; -#endif if (DoesTextInput || (focusedComponent_ && focusedComponent_->Visible && focusedComponent_->Enabled && focusedComponent_->DoesTextInput)) { @@ -310,8 +273,7 @@ void Window::DoTick(float dt) void Window::DoKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) { -#ifdef DEBUG - if (key == SDLK_TAB && ctrl) + if (DEBUG && key == SDLK_TAB && ctrl) debugMode = !debugMode; if (debugMode) { @@ -394,7 +356,6 @@ void Window::DoKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, b } return; } -#endif //on key press if (focusedComponent_ != NULL) { @@ -417,10 +378,8 @@ void Window::DoKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, b void Window::DoKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) { -#ifdef DEBUG if(debugMode) return; -#endif //on key unpress if (focusedComponent_ != NULL) { @@ -436,10 +395,8 @@ void Window::DoKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, void Window::DoTextInput(String text) { -#ifdef DEBUG if (debugMode) return; -#endif //on key unpress if (focusedComponent_ != NULL) { @@ -481,10 +438,10 @@ void Window::DoMouseDown(int x_, int y_, unsigned button) 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) { FocusComponent(Components[i]); -#ifdef DEBUG - if (!debugMode) -#endif - Components[i]->OnMouseClick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button); + if (!DEBUG || !debugMode) + { + Components[i]->OnMouseClick(x - Components[i]->Position.X, y - Components[i]->Position.Y, button); + } clickState = true; break; } @@ -494,10 +451,8 @@ void Window::DoMouseDown(int x_, int y_, unsigned button) if (!clickState) FocusComponent(NULL); -#ifdef DEBUG if (debugMode) return; -#endif //on mouse down for (int i = Components.size() - 1; i > -1 && !halt; --i) @@ -521,10 +476,8 @@ void Window::DoMouseMove(int x_, int y_, int dx, int dy) //on mouse move (if true, and inside) int x = x_ - Position.X; int y = y_ - Position.Y; -#ifdef DEBUG if (debugMode) return; -#endif for (int i = Components.size() - 1; i > -1 && !halt; --i) { if (Components[i]->Enabled && Components[i]->Visible) @@ -577,10 +530,8 @@ void Window::DoMouseUp(int x_, int y_, unsigned button) { int x = x_ - Position.X; int y = y_ - Position.Y; -#ifdef DEBUG if (debugMode) return; -#endif //on mouse unclick for (int i = Components.size() - 1; i >= 0 && !halt; --i) { @@ -611,10 +562,8 @@ void Window::DoMouseWheel(int x_, int y_, int d) { int x = x_ - Position.X; int y = y_ - Position.Y; -#ifdef DEBUG if (debugMode) return; -#endif //on mouse wheel focused for (int i = Components.size() - 1; i >= 0 && !halt; --i) { diff --git a/src/gui/interface/Window.h b/src/gui/interface/Window.h index 15f70cafc..66d84427f 100644 --- a/src/gui/interface/Window.h +++ b/src/gui/interface/Window.h @@ -1,9 +1,7 @@ -#ifndef WINDOW_H -#define WINDOW_H - +#pragma once #include "common/String.h" -#include #include "gui/interface/Point.h" +#include class Graphics; namespace ui @@ -115,9 +113,7 @@ namespace ui Component *hoverComponent; ChromeStyle chrome; -#ifdef DEBUG bool debugMode; -#endif //These controls allow a component to call the destruction of the Window inside an event (called by the Window) void finalise(); bool halt; @@ -126,4 +122,3 @@ namespace ui }; } -#endif // WINDOW_H diff --git a/src/gui/localbrowser/LocalBrowserController.cpp b/src/gui/localbrowser/LocalBrowserController.cpp index bbadf38fc..1f0092117 100644 --- a/src/gui/localbrowser/LocalBrowserController.cpp +++ b/src/gui/localbrowser/LocalBrowserController.cpp @@ -4,6 +4,8 @@ #include "LocalBrowserView.h" #include "client/Client.h" +#include "client/GameSave.h" +#include "client/SaveFile.h" #include "gui/dialogues/ConfirmPrompt.h" #include "tasks/TaskWindow.h" #include "tasks/Task.h" @@ -25,14 +27,14 @@ LocalBrowserController::LocalBrowserController(std::function onDone_): browserModel->UpdateSavesList(1); } -void LocalBrowserController::OpenSave(SaveFile * save) +void LocalBrowserController::OpenSave(int index) { - browserModel->SetSave(save); + browserModel->OpenSave(index); } -SaveFile * LocalBrowserController::GetSave() +std::unique_ptr LocalBrowserController::TakeSave() { - return browserModel->GetSave(); + return browserModel->TakeSave(); } void LocalBrowserController::RemoveSelected() @@ -65,7 +67,6 @@ void LocalBrowserController::removeSelectedC() } void after() override { - Client::Ref().updateStamps(); c->RefreshSavesList(); } }; @@ -75,11 +76,6 @@ void LocalBrowserController::removeSelectedC() } void LocalBrowserController::RescanStamps() -{ - new ConfirmPrompt("Rescan", "Rescanning the stamps folder can find stamps added to the stamps folder or recover stamps when the stamps.def file has been lost or damaged. However, be warned that this will mess up the current sorting order", { [this] { rescanStampsC(); } }); -} - -void LocalBrowserController::rescanStampsC() { browserModel->RescanStamps(); browserModel->UpdateSavesList(browserModel->GetPageNum()); @@ -111,7 +107,7 @@ void LocalBrowserController::SetPageRelative(int offset) void LocalBrowserController::Update() { - if(browserModel->GetSave()) + if (browserModel->GetSave()) { Exit(); } @@ -145,8 +141,10 @@ void LocalBrowserController::Exit() LocalBrowserController::~LocalBrowserController() { - browserView->CloseActiveWindow(); delete browserModel; - delete browserView; + if (browserView->CloseActiveWindow()) + { + delete browserView; + } } diff --git a/src/gui/localbrowser/LocalBrowserController.h b/src/gui/localbrowser/LocalBrowserController.h index e1050db11..f6de05a6a 100644 --- a/src/gui/localbrowser/LocalBrowserController.h +++ b/src/gui/localbrowser/LocalBrowserController.h @@ -1,10 +1,7 @@ -#ifndef STAMPSCONTROLLER_H_ -#define STAMPSCONTROLLER_H_ -#include "Config.h" - +#pragma once #include "common/String.h" - #include +#include class SaveFile; class LocalBrowserView; @@ -17,15 +14,14 @@ public: bool HasDone; LocalBrowserController(std::function onDone = nullptr); LocalBrowserView * GetView() {return browserView;} - SaveFile * GetSave(); + std::unique_ptr TakeSave(); void RemoveSelected(); void removeSelectedC(); void ClearSelection(); void Selected(ByteString stampID, bool selected); void RescanStamps(); - void rescanStampsC(); void RefreshSavesList(); - void OpenSave(SaveFile * stamp); + void OpenSave(int index); bool GetMoveToFront(); void SetMoveToFront(bool move); void SetPage(int page); @@ -34,5 +30,3 @@ public: void Exit(); virtual ~LocalBrowserController(); }; - -#endif /* STAMPSCONTROLLER_H_ */ diff --git a/src/gui/localbrowser/LocalBrowserModel.cpp b/src/gui/localbrowser/LocalBrowserModel.cpp index fcae7fa8a..627a0d33a 100644 --- a/src/gui/localbrowser/LocalBrowserModel.cpp +++ b/src/gui/localbrowser/LocalBrowserModel.cpp @@ -1,27 +1,28 @@ #include "LocalBrowserModel.h" - #include "LocalBrowserView.h" - -#include - #include "client/Client.h" #include "client/SaveFile.h" - +#include "client/GameSave.h" #include "common/tpt-minmax.h" +#include + +constexpr auto pageSize = 20; LocalBrowserModel::LocalBrowserModel(): - stamp(NULL), currentPage(1), stampToFront(1) { - //stampIDs = Client::Ref().GetStamps(); - stampIDs = Client::Ref().GetStamps(0, 16); + stampIDs = Client::Ref().GetStamps(); } -std::vector LocalBrowserModel::GetSavesList() +std::vector LocalBrowserModel::GetSavesList() // non-owning { - return savesList; + std::vector nonOwningSaveList; + std::transform(savesList.begin(), savesList.end(), std::back_inserter(nonOwningSaveList), [](auto &ptr) { + return ptr.get(); + }); + return nonOwningSaveList; } void LocalBrowserModel::AddObserver(LocalBrowserView * observer) @@ -48,15 +49,20 @@ void LocalBrowserModel::notifyPageChanged() } } -SaveFile * LocalBrowserModel::GetSave() +const SaveFile *LocalBrowserModel::GetSave() { - return stamp; + return stamp.get(); } -void LocalBrowserModel::SetSave(SaveFile * newStamp) +std::unique_ptr LocalBrowserModel::TakeSave() { - delete stamp; - stamp = new SaveFile(*newStamp); + return std::move(stamp); +} + +void LocalBrowserModel::OpenSave(int index) +{ + stamp = std::move(savesList[index]); + savesList.clear(); } bool LocalBrowserModel::GetMoveToFront() @@ -71,25 +77,19 @@ void LocalBrowserModel::SetMoveToFront(bool move) void LocalBrowserModel::UpdateSavesList(int pageNumber) { - std::vector tempSavesList = savesList; savesList.clear(); currentPage = pageNumber; notifyPageChanged(); notifySavesListChanged(); - //notifyStampsListChanged(); - /*for(int i = 0; i < tempSavesList.size(); i++) - { - delete tempSavesList[i]; - }*/ - stampIDs = Client::Ref().GetStamps((pageNumber-1)*20, 20); - - for (size_t i = 0; i < stampIDs.size(); i++) + stampIDs = Client::Ref().GetStamps(); + auto size = int(stampIDs.size()); + for (int i = (currentPage - 1) * pageSize; i < size && i < currentPage * pageSize; i++) { - SaveFile * tempSave = Client::Ref().GetStamp(stampIDs[i]); + auto tempSave = Client::Ref().GetStamp(stampIDs[i]); if (tempSave) { - savesList.push_back(tempSave); + savesList.push_back(std::move(tempSave)); } } notifySavesListChanged(); @@ -102,17 +102,15 @@ void LocalBrowserModel::RescanStamps() int LocalBrowserModel::GetPageCount() { - return std::max(1, (int)(std::ceil(float(Client::Ref().GetStampsCount())/20.0f))); + auto size = int(stampIDs.size()); + return size / pageSize + ((size % pageSize) ? 1 : 0); } void LocalBrowserModel::SelectSave(ByteString stampID) { - for (size_t i = 0; i < selected.size(); i++) + if (std::find(selected.begin(), selected.end(), stampID) != selected.end()) { - if (selected[i] == stampID) - { - return; - } + return; } selected.push_back(stampID); notifySelectedChanged(); @@ -120,19 +118,12 @@ void LocalBrowserModel::SelectSave(ByteString stampID) void LocalBrowserModel::DeselectSave(ByteString stampID) { - bool changed = false; -restart: - for (size_t i = 0; i < selected.size(); i++) + auto it = std::remove(selected.begin(), selected.end(), stampID); + if (it != selected.end()) { - if (selected[i] == stampID) - { - selected.erase(selected.begin()+i); - changed = true; - goto restart; //Just ensure all cases are removed. - } - } - if(changed) + selected.erase(it, selected.end()); notifySelectedChanged(); + } } void LocalBrowserModel::notifySelectedChanged() @@ -143,8 +134,3 @@ void LocalBrowserModel::notifySelectedChanged() cObserver->NotifySelectedChanged(this); } } - -LocalBrowserModel::~LocalBrowserModel() { - delete stamp; -} - diff --git a/src/gui/localbrowser/LocalBrowserModel.h b/src/gui/localbrowser/LocalBrowserModel.h index 613283c0f..7dd9ae0d4 100644 --- a/src/gui/localbrowser/LocalBrowserModel.h +++ b/src/gui/localbrowser/LocalBrowserModel.h @@ -1,18 +1,16 @@ -#ifndef STAMPSMODEL_H_ -#define STAMPSMODEL_H_ -#include "Config.h" - -#include +#pragma once #include "common/String.h" +#include +#include class SaveFile; class LocalBrowserView; class LocalBrowserModel { std::vector selected; - SaveFile * stamp; + std::unique_ptr stamp; std::vector stampIDs; - std::vector savesList; + std::vector> savesList; std::vector observers; int currentPage; bool stampToFront; @@ -24,18 +22,16 @@ public: int GetPageCount(); int GetPageNum() { return currentPage; } void AddObserver(LocalBrowserView * observer); - std::vector GetSavesList(); + std::vector GetSavesList(); // non-owning void UpdateSavesList(int pageNumber); void RescanStamps(); - SaveFile * GetSave(); - void SetSave(SaveFile * newStamp); + const SaveFile *GetSave(); + std::unique_ptr TakeSave(); + void OpenSave(int index); bool GetMoveToFront(); void SetMoveToFront(bool move); std::vector GetSelected() { return selected; } void ClearSelected() { selected.clear(); notifySelectedChanged(); } void SelectSave(ByteString stampID); void DeselectSave(ByteString stampID); - virtual ~LocalBrowserModel(); }; - -#endif /* STAMPSMODEL_H_ */ diff --git a/src/gui/localbrowser/LocalBrowserModelException.h b/src/gui/localbrowser/LocalBrowserModelException.h index 4da4ac359..f60a0dd0d 100644 --- a/src/gui/localbrowser/LocalBrowserModelException.h +++ b/src/gui/localbrowser/LocalBrowserModelException.h @@ -1,6 +1,4 @@ -#ifndef STAMPSMODELEXCEPTION_H_ -#define STAMPSMODELEXCEPTION_H_ - +#pragma once #include "common/String.h" #include @@ -12,5 +10,3 @@ public: const char * what() const throw() { return message.c_str(); }; ~LocalBrowserModelException() throw() {}; }; - -#endif /* STAMPSMODELEXCEPTION_H_ */ diff --git a/src/gui/localbrowser/LocalBrowserView.cpp b/src/gui/localbrowser/LocalBrowserView.cpp index 04a560569..99b181b07 100644 --- a/src/gui/localbrowser/LocalBrowserView.cpp +++ b/src/gui/localbrowser/LocalBrowserView.cpp @@ -1,20 +1,15 @@ #include "LocalBrowserView.h" - #include "LocalBrowserController.h" #include "LocalBrowserModel.h" - #include "gui/interface/Button.h" #include "gui/interface/Textbox.h" #include "gui/interface/Label.h" #include "gui/interface/SaveButton.h" -#include "gui/interface/Keys.h" - -#include "PowderToy.h" -#include "Config.h" - +#include "PowderToySDL.h" #include "client/SaveFile.h" - #include "graphics/Graphics.h" +#include "SimulationConfig.h" +#include LocalBrowserView::LocalBrowserView(): ui::Window(ui::Point(0, 0), ui::Point(WINDOWW, WINDOWH)), @@ -88,7 +83,7 @@ void LocalBrowserView::NotifyPageChanged(LocalBrowserModel * sender) { String pageInfo = String::Build("of ", pageCount); pageCountLabel->SetText(pageInfo); - int width = Graphics::textwidth(pageInfo); + int width = Graphics::TextSize(pageInfo).X - 1; pageLabel->Position.X = WINDOWW/2-width-20; pageTextbox->Position.X = WINDOWW/2-width+11; @@ -123,7 +118,7 @@ void LocalBrowserView::NotifySavesListChanged(LocalBrowserModel * sender) int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 2; int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; - std::vector saves = sender->GetSavesList(); + auto saves = sender->GetSavesList(); // non-owning for (size_t i = 0; i < stampButtons.size(); i++) { RemoveComponent(stampButtons[i]); @@ -136,7 +131,7 @@ void LocalBrowserView::NotifySavesListChanged(LocalBrowserModel * sender) buttonAreaHeight = Size.Y - buttonYOffset - 18; buttonWidth = (buttonAreaWidth/savesX) - buttonPadding*2; buttonHeight = (buttonAreaHeight/savesY) - buttonPadding*2; - for (size_t i = 0; i < saves.size(); i++) + for (auto i = 0; i < int(saves.size()); i++) { if(saveX == savesX) { @@ -155,9 +150,9 @@ void LocalBrowserView::NotifySavesListChanged(LocalBrowserModel * sender) saves[i]); saveButton->SetSelectable(true); saveButton->SetActionCallback({ - [this, saveButton] { + [this, saveButton, i] { if (saveButton->GetSaveFile()) - c->OpenSave(saveButton->GetSaveFile()); + c->OpenSave(i); }, nullptr, nullptr, diff --git a/src/gui/localbrowser/LocalBrowserView.h b/src/gui/localbrowser/LocalBrowserView.h index 225cdced8..96c05ce58 100644 --- a/src/gui/localbrowser/LocalBrowserView.h +++ b/src/gui/localbrowser/LocalBrowserView.h @@ -1,8 +1,6 @@ -#ifndef STAMPSVIEW_H_ -#define STAMPSVIEW_H_ - -#include +#pragma once #include "gui/interface/Window.h" +#include namespace ui { @@ -42,5 +40,3 @@ public: void OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override; virtual ~LocalBrowserView(); }; - -#endif /* STAMPSVIEW_H_ */ diff --git a/src/gui/login/LoginController.cpp b/src/gui/login/LoginController.cpp index 5a02b3c5a..2d53c7cd5 100644 --- a/src/gui/login/LoginController.cpp +++ b/src/gui/login/LoginController.cpp @@ -1,7 +1,7 @@ #include "LoginController.h" - #include "client/Client.h" - +#include "client/http/LoginRequest.h" +#include "client/http/LogoutRequest.h" #include "LoginView.h" #include "LoginModel.h" #include "Controller.h" @@ -23,15 +23,19 @@ void LoginController::Login(ByteString username, ByteString password) loginModel->Login(username, password); } -User LoginController::GetUser() +void LoginController::Logout() { - return loginModel->GetUser(); + loginModel->Logout(); +} + +void LoginController::Tick() +{ + loginModel->Tick(); } void LoginController::Exit() { loginView->CloseActiveWindow(); - Client::Ref().SetAuthUser(loginModel->GetUser()); if (onDone) onDone(); HasExited = true; @@ -39,8 +43,10 @@ void LoginController::Exit() LoginController::~LoginController() { - loginView->CloseActiveWindow(); delete loginModel; - delete loginView; + if (loginView->CloseActiveWindow()) + { + delete loginView; + } } diff --git a/src/gui/login/LoginController.h b/src/gui/login/LoginController.h index 7e3929dad..a81ede74f 100644 --- a/src/gui/login/LoginController.h +++ b/src/gui/login/LoginController.h @@ -1,10 +1,6 @@ -#ifndef LOGINCONTROLLER_H_ -#define LOGINCONTROLLER_H_ -#include "Config.h" - +#pragma once #include "common/String.h" #include "client/User.h" - #include class LoginView; @@ -18,10 +14,9 @@ public: bool HasExited; LoginController(std::function onDone = nullptr); void Login(ByteString username, ByteString password); + void Logout(); + void Tick(); void Exit(); LoginView * GetView() { return loginView; } - User GetUser(); - virtual ~LoginController(); + ~LoginController(); }; - -#endif /* LOGINCONTROLLER_H_ */ diff --git a/src/gui/login/LoginModel.cpp b/src/gui/login/LoginModel.cpp index 970d95862..b5d591fb2 100644 --- a/src/gui/login/LoginModel.cpp +++ b/src/gui/login/LoginModel.cpp @@ -1,39 +1,32 @@ #include "LoginModel.h" - #include "LoginView.h" - #include "client/Client.h" - -LoginModel::LoginModel(): - currentUser(0, "") -{ - -} +#include "client/http/LoginRequest.h" +#include "client/http/LogoutRequest.h" 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"; - loginStatus = false; + loginStatus = loginIdle; notifyStatusChanged(); return; } statusText = "Logging in..."; - loginStatus = false; + loginStatus = loginWorking; notifyStatusChanged(); - LoginStatus status = Client::Ref().Login(username, password, currentUser); - switch(status) - { - case LoginOkay: - statusText = "Logged in"; - loginStatus = true; - break; - case LoginError: - statusText = Client::Ref().GetLastError(); - break; - } + loginRequest = std::make_unique(username, password); + loginRequest->Start(); +} + +void LoginModel::Logout() +{ + statusText = "Logging out..."; + loginStatus = loginWorking; notifyStatusChanged(); + logoutRequest = std::make_unique(); + logoutRequest->Start(); } void LoginModel::AddObserver(LoginView * observer) @@ -46,14 +39,47 @@ String LoginModel::GetStatusText() return statusText; } -User LoginModel::GetUser() +void LoginModel::Tick() { - return currentUser; -} - -bool LoginModel::GetStatus() -{ - return loginStatus; + if (loginRequest && loginRequest->CheckDone()) + { + try + { + auto info = loginRequest->Finish(); + auto &client = Client::Ref(); + client.SetAuthUser(info.user); + for (auto &item : info.notifications) + { + client.AddServerNotification(item); + } + statusText = "Logged in"; + loginStatus = loginSucceeded; + } + catch (const http::RequestError &ex) + { + statusText = ByteString(ex.what()).FromUtf8(); + loginStatus = loginIdle; + } + notifyStatusChanged(); + loginRequest.reset(); + } + if (logoutRequest && logoutRequest->CheckDone()) + { + try + { + logoutRequest->Finish(); + auto &client = Client::Ref(); + client.SetAuthUser(User(0, "")); + statusText = "Logged out"; + } + catch (const http::RequestError &ex) + { + statusText = ByteString(ex.what()).FromUtf8(); + } + loginStatus = loginIdle; + notifyStatusChanged(); + logoutRequest.reset(); + } } void LoginModel::notifyStatusChanged() @@ -64,6 +90,7 @@ void LoginModel::notifyStatusChanged() } } -LoginModel::~LoginModel() { +LoginModel::~LoginModel() +{ + // Satisfy std::unique_ptr } - diff --git a/src/gui/login/LoginModel.h b/src/gui/login/LoginModel.h index 3d4547be2..3a8f229dc 100644 --- a/src/gui/login/LoginModel.h +++ b/src/gui/login/LoginModel.h @@ -1,27 +1,42 @@ -#ifndef LOGINMODEL_H_ -#define LOGINMODEL_H_ -#include "Config.h" - -#include +#pragma once #include "common/String.h" #include "client/User.h" +#include +#include + +namespace http +{ + class LoginRequest; + class LogoutRequest; +} + +enum LoginStatus +{ + loginIdle, + loginWorking, + loginSucceeded, +}; class LoginView; class LoginModel { + std::unique_ptr loginRequest; + std::unique_ptr logoutRequest; std::vector observers; String statusText; - bool loginStatus; + LoginStatus loginStatus = loginIdle; void notifyStatusChanged(); - User currentUser; + public: - LoginModel(); void Login(ByteString username, ByteString password); + void Logout(); void AddObserver(LoginView * observer); String GetStatusText(); - bool GetStatus(); + LoginStatus GetStatus() const + { + return loginStatus; + } + void Tick(); User GetUser(); - virtual ~LoginModel(); + ~LoginModel(); }; - -#endif /* LOGINMODEL_H_ */ diff --git a/src/gui/login/LoginView.cpp b/src/gui/login/LoginView.cpp index faba2b8f9..1ec8e5077 100644 --- a/src/gui/login/LoginView.cpp +++ b/src/gui/login/LoginView.cpp @@ -1,18 +1,14 @@ #include "LoginView.h" - #include "LoginModel.h" #include "LoginController.h" - #include "graphics/Graphics.h" #include "gui/interface/Button.h" #include "gui/interface/Label.h" #include "gui/interface/Textbox.h" -#include "gui/interface/Keys.h" #include "gui/Style.h" - #include "client/Client.h" - #include "Misc.h" +#include LoginView::LoginView(): ui::Window(ui::Point(-1, -1), ui::Point(200, 87)), @@ -38,11 +34,15 @@ LoginView::LoginView(): loginButton->Appearance.HorizontalAlign = ui::Appearance::AlignRight; loginButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; loginButton->Appearance.TextInactive = style::Colour::ConfirmButton; - loginButton->SetActionCallback({ [this] { c->Login(usernameField->GetText().ToUtf8(), passwordField->GetText().ToUtf8()); } }); + loginButton->SetActionCallback({ [this] { + c->Login(usernameField->GetText().ToUtf8(), passwordField->GetText().ToUtf8()); + } }); AddComponent(cancelButton); cancelButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; cancelButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - cancelButton->SetActionCallback({ [this] { c->Exit(); } }); + cancelButton->SetActionCallback({ [this] { + c->Logout(); + } }); AddComponent(titleLabel); titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; @@ -84,12 +84,17 @@ void LoginView::NotifyStatusChanged(LoginModel * sender) targetSize.Y = 87; infoLabel->SetText(sender->GetStatusText()); infoLabel->AutoHeight(); + auto notWorking = sender->GetStatus() != loginWorking; + loginButton->Enabled = notWorking; + cancelButton->Enabled = notWorking && Client::Ref().GetAuthUser().UserID; + usernameField->Enabled = notWorking; + passwordField->Enabled = notWorking; if (sender->GetStatusText().length()) { targetSize.Y += infoLabel->Size.Y+2; infoLabel->Visible = true; } - if(sender->GetStatus()) + if (sender->GetStatus() == loginSucceeded) { c->Exit(); } @@ -97,6 +102,7 @@ void LoginView::NotifyStatusChanged(LoginModel * sender) void LoginView::OnTick(float dt) { + c->Tick(); //if(targetSize != Size) { ui::Point difference = targetSize-Size; @@ -123,8 +129,8 @@ void LoginView::OnTick(float dt) void LoginView::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); } LoginView::~LoginView() { diff --git a/src/gui/login/LoginView.h b/src/gui/login/LoginView.h index 589d0093e..b2287a021 100644 --- a/src/gui/login/LoginView.h +++ b/src/gui/login/LoginView.h @@ -1,6 +1,4 @@ -#ifndef LOGINVIEW_H_ -#define LOGINVIEW_H_ - +#pragma once #include "gui/interface/Window.h" namespace ui @@ -32,5 +30,3 @@ public: void OnTick(float dt) override; virtual ~LoginView(); }; - -#endif /* LOGINVIEW_H_ */ diff --git a/src/gui/options/OptionsController.cpp b/src/gui/options/OptionsController.cpp index 15393233a..4a67e2bd1 100644 --- a/src/gui/options/OptionsController.cpp +++ b/src/gui/options/OptionsController.cpp @@ -97,6 +97,11 @@ void OptionsController::SetScale(int scale) model->SetScale(scale); } +void OptionsController::SetGraveExitsConsole(bool graveExitsConsole) +{ + model->SetGraveExitsConsole(graveExitsConsole); +} + void OptionsController::SetResizable(bool resizable) { model->SetResizable(resizable); @@ -149,8 +154,10 @@ void OptionsController::Exit() OptionsController::~OptionsController() { - view->CloseActiveWindow(); delete model; - delete view; + if (view->CloseActiveWindow()) + { + delete view; + } } diff --git a/src/gui/options/OptionsController.h b/src/gui/options/OptionsController.h index b7f98dd3d..d211f981c 100644 --- a/src/gui/options/OptionsController.h +++ b/src/gui/options/OptionsController.h @@ -1,7 +1,4 @@ -#ifndef OPTIONSCONTROLLER_H_ -#define OPTIONSCONTROLLER_H_ -#include "Config.h" - +#pragma once #include class GameModel; @@ -31,6 +28,7 @@ public: void SetAltFullscreen(bool altFullscreen); void SetForceIntegerScaling(bool forceIntegerScaling); void SetScale(int scale); + void SetGraveExitsConsole(bool graveExitsConsole); void SetResizable(bool resizable); void SetFastQuit(bool fastquit); void SetDecoSpace(int decoSpace); @@ -44,5 +42,3 @@ public: OptionsView * GetView(); virtual ~OptionsController(); }; - -#endif /* OPTIONSCONTROLLER_H_ */ diff --git a/src/gui/options/OptionsModel.cpp b/src/gui/options/OptionsModel.cpp index 5005873f3..7c49960b1 100644 --- a/src/gui/options/OptionsModel.cpp +++ b/src/gui/options/OptionsModel.cpp @@ -4,9 +4,9 @@ #include "simulation/Simulation.h" #include "simulation/Air.h" -#include "simulation/Gravity.h" +#include "simulation/gravity/Gravity.h" -#include "client/Client.h" +#include "prefs/GlobalPrefs.h" #include "gui/interface/Engine.h" #include "gui/game/GameModel.h" @@ -85,7 +85,7 @@ int OptionsModel::GetEdgeMode() } void OptionsModel::SetEdgeMode(int edgeMode) { - Client::Ref().SetPref("Simulation.EdgeMode", edgeMode); + GlobalPrefs::Ref().Set("Simulation.EdgeMode", edgeMode); gModel->SetEdgeMode(edgeMode); notifySettingsChanged(); } @@ -96,7 +96,7 @@ int OptionsModel::GetTemperatureScale() } void OptionsModel::SetTemperatureScale(int temperatureScale) { - Client::Ref().SetPref("Renderer.TemperatureScale", temperatureScale); + GlobalPrefs::Ref().Set("Renderer.TemperatureScale", temperatureScale); gModel->SetTemperatureScale(temperatureScale); notifySettingsChanged(); } @@ -107,7 +107,7 @@ float OptionsModel::GetAmbientAirTemperature() } void OptionsModel::SetAmbientAirTemperature(float ambientAirTemp) { - Client::Ref().SetPref("Simulation.AmbientAirTemp", ambientAirTemp); + GlobalPrefs::Ref().Set("Simulation.AmbientAirTemp", ambientAirTemp); gModel->SetAmbientAirTemperature(ambientAirTemp); notifySettingsChanged(); } @@ -152,7 +152,19 @@ int OptionsModel::GetScale() void OptionsModel::SetScale(int scale) { ui::Engine::Ref().SetScale(scale); - Client::Ref().SetPref("Scale", int(scale)); + GlobalPrefs::Ref().Set("Scale", int(scale)); + notifySettingsChanged(); +} + +bool OptionsModel::GetGraveExitsConsole() +{ + return ui::Engine::Ref().GraveExitsConsole; +} + +void OptionsModel::SetGraveExitsConsole(bool graveExitsConsole) +{ + ui::Engine::Ref().GraveExitsConsole = graveExitsConsole; + GlobalPrefs::Ref().Set("GraveExitsConsole", graveExitsConsole); notifySettingsChanged(); } @@ -164,7 +176,7 @@ bool OptionsModel::GetResizable() void OptionsModel::SetResizable(bool resizable) { ui::Engine::Ref().SetResizable(resizable); - Client::Ref().SetPref("Resizable", resizable); + GlobalPrefs::Ref().Set("Resizable", resizable); notifySettingsChanged(); } @@ -175,7 +187,7 @@ bool OptionsModel::GetFullscreen() void OptionsModel::SetFullscreen(bool fullscreen) { ui::Engine::Ref().SetFullscreen(fullscreen); - Client::Ref().SetPref("Fullscreen", fullscreen); + GlobalPrefs::Ref().Set("Fullscreen", fullscreen); notifySettingsChanged(); } @@ -187,7 +199,7 @@ bool OptionsModel::GetAltFullscreen() void OptionsModel::SetAltFullscreen(bool altFullscreen) { ui::Engine::Ref().SetAltFullscreen(altFullscreen); - Client::Ref().SetPref("AltFullscreen", altFullscreen); + GlobalPrefs::Ref().Set("AltFullscreen", altFullscreen); notifySettingsChanged(); } @@ -199,7 +211,7 @@ bool OptionsModel::GetForceIntegerScaling() void OptionsModel::SetForceIntegerScaling(bool forceIntegerScaling) { ui::Engine::Ref().SetForceIntegerScaling(forceIntegerScaling); - Client::Ref().SetPref("ForceIntegerScaling", forceIntegerScaling); + GlobalPrefs::Ref().Set("ForceIntegerScaling", forceIntegerScaling); notifySettingsChanged(); } @@ -210,7 +222,7 @@ bool OptionsModel::GetFastQuit() void OptionsModel::SetFastQuit(bool fastquit) { ui::Engine::Ref().SetFastQuit(fastquit); - Client::Ref().SetPref("FastQuit", bool(fastquit)); + GlobalPrefs::Ref().Set("FastQuit", bool(fastquit)); notifySettingsChanged(); } @@ -220,7 +232,7 @@ int OptionsModel::GetDecoSpace() } void OptionsModel::SetDecoSpace(int decoSpace) { - Client::Ref().SetPref("Simulation.DecoSpace", decoSpace); + GlobalPrefs::Ref().Set("Simulation.DecoSpace", decoSpace); gModel->SetDecoSpace(decoSpace); notifySettingsChanged(); } @@ -233,7 +245,7 @@ bool OptionsModel::GetShowAvatars() void OptionsModel::SetShowAvatars(bool state) { ui::Engine::Ref().ShowAvatars = state; - Client::Ref().SetPref("ShowAvatars", state); + GlobalPrefs::Ref().Set("ShowAvatars", state); notifySettingsChanged(); } @@ -244,7 +256,7 @@ bool OptionsModel::GetMouseClickRequired() void OptionsModel::SetMouseClickRequired(bool mouseClickRequired) { - Client::Ref().SetPref("MouseClickRequired", mouseClickRequired); + GlobalPrefs::Ref().Set("MouseClickRequired", mouseClickRequired); gModel->SetMouseClickRequired(mouseClickRequired); notifySettingsChanged(); } @@ -256,7 +268,7 @@ bool OptionsModel::GetIncludePressure() void OptionsModel::SetIncludePressure(bool includePressure) { - Client::Ref().SetPref("Simulation.IncludePressure", includePressure); + GlobalPrefs::Ref().Set("Simulation.IncludePressure", includePressure); gModel->SetIncludePressure(includePressure); notifySettingsChanged(); } @@ -268,7 +280,7 @@ bool OptionsModel::GetPerfectCircle() void OptionsModel::SetPerfectCircle(bool perfectCircle) { - Client::Ref().SetPref("PerfectCircleBrush", perfectCircle); + GlobalPrefs::Ref().Set("PerfectCircleBrush", perfectCircle); gModel->SetPerfectCircle(perfectCircle); notifySettingsChanged(); } @@ -280,7 +292,7 @@ bool OptionsModel::GetMomentumScroll() void OptionsModel::SetMomentumScroll(bool state) { - Client::Ref().SetPref("MomentumScroll", state); + GlobalPrefs::Ref().Set("MomentumScroll", state); ui::Engine::Ref().MomentumScroll = state; notifySettingsChanged(); } diff --git a/src/gui/options/OptionsModel.h b/src/gui/options/OptionsModel.h index 105f03640..23f340471 100644 --- a/src/gui/options/OptionsModel.h +++ b/src/gui/options/OptionsModel.h @@ -1,7 +1,4 @@ -#ifndef OPTIONSMODEL_H_ -#define OPTIONSMODEL_H_ -#include "Config.h" - +#pragma once #include class GameModel; @@ -42,6 +39,8 @@ public: void SetCustomGravityY(float y); int GetScale(); void SetScale(int scale); + bool GetGraveExitsConsole(); + void SetGraveExitsConsole(bool graveExitsConsole); bool GetResizable(); void SetResizable(bool resizable); bool GetFullscreen(); @@ -64,5 +63,3 @@ public: void SetMomentumScroll(bool momentumScroll); virtual ~OptionsModel(); }; - -#endif /* OPTIONSMODEL_H_ */ diff --git a/src/gui/options/OptionsView.cpp b/src/gui/options/OptionsView.cpp index 6de3fe2d2..f3da7f231 100644 --- a/src/gui/options/OptionsView.cpp +++ b/src/gui/options/OptionsView.cpp @@ -1,19 +1,13 @@ #include "OptionsView.h" - -#include -#include -#include -#include "SDLCompat.h" #include "Format.h" - #include "OptionsController.h" #include "OptionsModel.h" - -#include "common/Platform.h" +#include "common/platform/Platform.h" #include "graphics/Graphics.h" +#include "graphics/Renderer.h" #include "gui/Style.h" #include "simulation/ElementDefs.h" - +#include "client/Client.h" #include "gui/dialogues/ConfirmPrompt.h" #include "gui/dialogues/InformationMessage.h" #include "gui/interface/Button.h" @@ -23,21 +17,26 @@ #include "gui/interface/Label.h" #include "gui/interface/Textbox.h" #include "gui/interface/DirectionSelector.h" +#include "PowderToySDL.h" +#include +#include +#include +#include -OptionsView::OptionsView(): - ui::Window(ui::Point(-1, -1), ui::Point(320, 340)) - { - - auto autowidth = [this](ui::Component *c) { - c->Size.X = Size.X - c->Position.X - 12; +OptionsView::OptionsView() : ui::Window(ui::Point(-1, -1), ui::Point(320, 340)) +{ + auto autoWidth = [this](ui::Component *c, int extra) { + c->Size.X = Size.X - c->Position.X - 12 - extra; }; - ui::Label * tempLabel = new ui::Label(ui::Point(4, 1), ui::Point(Size.X-8, 22), "Simulation Options"); - tempLabel->SetTextColour(style::Colour::InformationTitle); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - autowidth(tempLabel); - AddComponent(tempLabel); + { + auto *label = new ui::Label(ui::Point(4, 1), ui::Point(Size.X-8, 22), "Simulation Options"); + label->SetTextColour(style::Colour::InformationTitle); + label->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + label->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + autoWidth(label, 0); + AddComponent(label); + } class Separator : public ui::Component { @@ -47,106 +46,97 @@ OptionsView::OptionsView(): void Draw(const ui::Point& screenPos) override { - GetGraphics()->drawrect(screenPos.X, screenPos.Y, Size.X, Size.Y, 255, 255, 255, 180); + GetGraphics()->BlendRect(RectSized(screenPos, Size), 0xFFFFFF_rgb .WithAlpha(180)); } }; Separator *tmpSeparator = new Separator(ui::Point(0, 22), ui::Point(Size.X, 1)); AddComponent(tmpSeparator); - int currentY = 6; scrollPanel = new ui::ScrollPanel(ui::Point(1, 23), ui::Point(Size.X-2, Size.Y-39)); AddComponent(scrollPanel); - heatSimulation = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Heat simulation \bgIntroduced in version 34", ""); - autowidth(heatSimulation); - heatSimulation->SetActionCallback({ [this] { c->SetHeatSimulation(heatSimulation->GetChecked()); } }); - scrollPanel->AddChild(heatSimulation); - currentY+=14; - tempLabel = new ui::Label(ui::Point(24, currentY), ui::Point(1, 16), "\bgCan cause odd behaviour when disabled"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=16; - ambientHeatSimulation = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Ambient heat simulation \bgIntroduced in version 50", ""); - autowidth(ambientHeatSimulation); - ambientHeatSimulation->SetActionCallback({ [this] { c->SetAmbientHeatSimulation(ambientHeatSimulation->GetChecked()); } }); - scrollPanel->AddChild(ambientHeatSimulation); - currentY+=14; - tempLabel = new ui::Label(ui::Point(24, currentY), ui::Point(1, 16), "\bgCan cause odd / broken behaviour with many saves"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=16; - newtonianGravity = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Newtonian gravity \bgIntroduced in version 48", ""); - autowidth(newtonianGravity); - newtonianGravity->SetActionCallback({ [this] { c->SetNewtonianGravity(newtonianGravity->GetChecked()); } }); - scrollPanel->AddChild(newtonianGravity); - currentY+=14; - tempLabel = new ui::Label(ui::Point(24, currentY), ui::Point(1, 16), "\bgMay cause poor performance on older computers"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=16; - waterEqualisation = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Water equalisation \bgIntroduced in version 61", ""); - autowidth(waterEqualisation); - waterEqualisation->SetActionCallback({ [this] { c->SetWaterEqualisation(waterEqualisation->GetChecked()); } }); - scrollPanel->AddChild(waterEqualisation); - currentY+=14; - tempLabel = new ui::Label(ui::Point(24, currentY), ui::Point(1, 16), "\bgMay cause poor performance with a lot of water"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=19; - airMode = new ui::DropDown(ui::Point(Size.X-95, currentY), ui::Point(80, 16)); - scrollPanel->AddChild(airMode); - airMode->AddOption(std::pair("On", 0)); - airMode->AddOption(std::pair("Pressure off", 1)); - airMode->AddOption(std::pair("Velocity off", 2)); - airMode->AddOption(std::pair("Off", 3)); - airMode->AddOption(std::pair("No Update", 4)); - airMode->SetActionCallback({ [this] { c->SetAirMode(airMode->GetOption().second); } }); - - tempLabel = new ui::Label(ui::Point(8, currentY), ui::Point(Size.X-96, 16), "Air Simulation Mode"); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=20; - ambientAirTemp = new ui::Textbox(ui::Point(Size.X-95, currentY), ui::Point(60, 16)); - ambientAirTemp->SetActionCallback({ [this] { - UpdateAirTemp(ambientAirTemp->GetText(), false); - } }); - ambientAirTemp->SetDefocusCallback({ [this] { - UpdateAirTemp(ambientAirTemp->GetText(), true); - }}); - scrollPanel->AddChild(ambientAirTemp); - - ambientAirTempPreview = new ui::Button(ui::Point(Size.X-31, currentY), ui::Point(16, 16), "", "Preview"); - scrollPanel->AddChild(ambientAirTempPreview); - - tempLabel = new ui::Label(ui::Point(8, currentY), ui::Point(Size.X-96, 16), "Ambient Air Temperature"); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=20; - gravityMode = new ui::DropDown(ui::Point(Size.X-95, currentY), ui::Point(80, 16)); - scrollPanel->AddChild(gravityMode); - gravityMode->AddOption(std::pair("Vertical", 0)); - gravityMode->AddOption(std::pair("Off", 1)); - gravityMode->AddOption(std::pair("Radial", 2)); - gravityMode->AddOption(std::pair("Custom", 3)); + int currentY = 8; + auto addCheckbox = [this, ¤tY, &autoWidth](int indent, String text, String info, std::function action) { + 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()) + { + auto *label = new ui::Label(ui::Point(22 + indent * 15, currentY), ui::Point(1, 16), "\bg" + info); + autoWidth(label, 0); + label->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + label->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + scrollPanel->AddChild(label); + currentY += 14; + } + currentY += 4; + return checkbox; + }; + auto addDropDown = [this, ¤tY, &autoWidth](String info, std::vector> options, std::function action) { + auto *dropDown = new ui::DropDown(ui::Point(Size.X - 95, currentY), ui::Point(80, 16)); + scrollPanel->AddChild(dropDown); + for (auto &option : options) + { + dropDown->AddOption(option); + } + dropDown->SetActionCallback({ action }); + auto *label = new ui::Label(ui::Point(8, currentY), ui::Point(Size.X - 96, 16), info); + label->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + label->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + scrollPanel->AddChild(label); + autoWidth(label, 85); + currentY += 20; + return dropDown; + }; + auto addSeparator = [this, ¤tY]() { + currentY += 6; + auto *separator = new Separator(ui::Point(0, currentY), ui::Point(Size.X, 1)); + scrollPanel->AddChild(separator); + currentY += 11; + }; + heatSimulation = addCheckbox(0, "Heat simulation \bgIntroduced in version 34", "Can cause odd behaviour when disabled", [this] { + c->SetHeatSimulation(heatSimulation->GetChecked()); + }); + newtonianGravity = addCheckbox(0, "Newtonian gravity \bgIntroduced in version 48", "May cause poor performance on older computers", [this] { + c->SetNewtonianGravity(newtonianGravity->GetChecked()); + }); + ambientHeatSimulation = addCheckbox(0, "Ambient heat simulation \bgIntroduced in version 50", "Can cause odd / broken behaviour with many saves", [this] { + c->SetAmbientHeatSimulation(ambientHeatSimulation->GetChecked()); + }); + waterEqualisation = addCheckbox(0, "Water equalisation \bgIntroduced in version 61", "May cause poor performance with a lot of water", [this] { + c->SetWaterEqualisation(waterEqualisation->GetChecked()); + }); + airMode = addDropDown("Air simulation mode", { + { "On", 0 }, + { "Pressure off", 1 }, + { "Velocity off", 2 }, + { "Off", 3 }, + { "No update", 4 }, + }, [this] { + c->SetAirMode(airMode->GetOption().second); + }); + { + ambientAirTemp = new ui::Textbox(ui::Point(Size.X-95, currentY), ui::Point(60, 16)); + ambientAirTemp->SetActionCallback({ [this] { + UpdateAirTemp(ambientAirTemp->GetText(), false); + } }); + ambientAirTemp->SetDefocusCallback({ [this] { + UpdateAirTemp(ambientAirTemp->GetText(), true); + }}); + 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"); + label->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; + label->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + scrollPanel->AddChild(label); + currentY += 20; + } class GravityWindow : public ui::Window { void OnTryExit(ExitMethod method) override @@ -159,8 +149,8 @@ OptionsView::OptionsView(): { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 200, 200, 200, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xC8C8C8_rgb); } ui::DirectionSelector * gravityDirection; @@ -211,226 +201,135 @@ OptionsView::OptionsView(): MakeActiveWindow(); } }; - - gravityMode->SetActionCallback({ [this] { + gravityMode = addDropDown("Gravity simulation mode", { + { "Vertical", 0 }, + { "Off", 1 }, + { "Radial", 2 }, + { "Custom", 3 }, + }, [this] { c->SetGravityMode(gravityMode->GetOption().second); if (gravityMode->GetOption().second == 3) + { new GravityWindow(ui::Point(-1, -1), 0.05f, 40, customGravityX, customGravityY, c); - } }); - - tempLabel = new ui::Label(ui::Point(8, currentY), ui::Point(Size.X-96, 16), "Gravity Simulation Mode"); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=20; - edgeMode = new ui::DropDown(ui::Point(Size.X-95, currentY), ui::Point(80, 16)); - scrollPanel->AddChild(edgeMode); - edgeMode->AddOption(std::pair("Void", 0)); - edgeMode->AddOption(std::pair("Solid", 1)); - edgeMode->AddOption(std::pair("Loop", 2)); - edgeMode->SetActionCallback({ [this] { c->SetEdgeMode(edgeMode->GetOption().second); } }); - - tempLabel = new ui::Label(ui::Point(8, currentY), ui::Point(Size.X-96, 16), "Edge Mode"); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=20; - temperatureScale = new ui::DropDown(ui::Point(Size.X-95, currentY), ui::Point(80, 16)); - scrollPanel->AddChild(temperatureScale); - temperatureScale->AddOption(std::pair("Kelvin", 0)); - temperatureScale->AddOption(std::pair("Celsius", 1)); - temperatureScale->AddOption(std::pair("Fahrenheit", 2)); - temperatureScale->SetActionCallback({ [this] { c->SetTemperatureScale(temperatureScale->GetOption().second); } }); - - tempLabel = new ui::Label(ui::Point(8, currentY), ui::Point(Size.X-96, 16), "Temperature Scale"); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=20; - tmpSeparator = new Separator(ui::Point(0, currentY), ui::Point(Size.X, 1)); - scrollPanel->AddChild(tmpSeparator); - - currentY+=4; - scale = new ui::DropDown(ui::Point(8, currentY), ui::Point(40, 16)); + } + }); + edgeMode = addDropDown("Edge mode", { + { "Void", 0 }, + { "Solid", 1 }, + { "Loop", 2 }, + }, [this] { + c->SetEdgeMode(edgeMode->GetOption().second); + }); + temperatureScale = addDropDown("Temperature scale", { + { "Kelvin", 0 }, + { "Celsius", 1 }, + { "Fahrenheit", 2 }, + }, [this] { + c->SetTemperatureScale(temperatureScale->GetOption().second); + }); + addSeparator(); { - int current_scale = ui::Engine::Ref().GetScale(); - int ix_scale = 1; - bool current_scale_valid = false; + std::vector> options; + int currentScale = ui::Engine::Ref().GetScale(); + int scaleIndex = 1; + bool currentScaleValid = false; do { - if (current_scale == ix_scale) - current_scale_valid = true; - scale->AddOption(std::pair(String::Build(ix_scale), ix_scale)); - ix_scale += 1; + if (currentScale == scaleIndex) + { + currentScaleValid = true; + } + options.push_back({ String::Build(scaleIndex), scaleIndex }); + scaleIndex += 1; } - while (ui::Engine::Ref().GetMaxWidth() >= ui::Engine::Ref().GetWidth() * ix_scale && ui::Engine::Ref().GetMaxHeight() >= ui::Engine::Ref().GetHeight() * ix_scale); - if (!current_scale_valid) - scale->AddOption(std::pair("current", current_scale)); + while (desktopWidth >= GetGraphics()->Size().X * scaleIndex && desktopHeight >= GetGraphics()->Size().Y * scaleIndex); + if (!currentScaleValid) + { + options.push_back({ "current", currentScale }); + } + scale = addDropDown("Window scale factor for larger screens", options, [this] { + c->SetScale(scale->GetOption().second); + }); } - scale->SetActionCallback({ [this] { c->SetScale(scale->GetOption().second); } }); - scrollPanel->AddChild(scale); + resizable = addCheckbox(0, "Resizable \bg- allow resizing and maximizing window", "", [this] { + c->SetResizable(resizable->GetChecked()); + }); + fullscreen = addCheckbox(0, "Fullscreen \bg- fill the entire screen", "", [this] { + c->SetFullscreen(fullscreen->GetChecked()); + }); + altFullscreen = addCheckbox(1, "Set optimal screen resolution", "", [this] { + c->SetAltFullscreen(altFullscreen->GetChecked()); + }); + forceIntegerScaling = addCheckbox(1, "Force integer scaling \bg- less blurry", "", [this] { + c->SetForceIntegerScaling(forceIntegerScaling->GetChecked()); + }); + addSeparator(); + fastquit = addCheckbox(0, "Fast quit", "Always exit completely when hitting close", [this] { + c->SetFastQuit(fastquit->GetChecked()); + }); + showAvatars = addCheckbox(0, "Show avatars", "Disable if you have a slow connection", [this] { + c->SetShowAvatars(showAvatars->GetChecked()); + }); + momentumScroll = addCheckbox(0, "Momentum (old) scrolling", "Accelerating instead of step scroll", [this] { + c->SetMomentumScroll(momentumScroll->GetChecked()); + }); + mouseClickRequired = addCheckbox(0, "Sticky categories", "Switch between categories by clicking", [this] { + c->SetMouseClickrequired(mouseClickRequired->GetChecked()); + }); + includePressure = addCheckbox(0, "Include pressure", "When saving, copying, stamping, etc.", [this] { + c->SetIncludePressure(includePressure->GetChecked()); + }); + perfectCircle = addCheckbox(0, "Perfect circle brush", "Better circle brush, without incorrect points on edges", [this] { + c->SetPerfectCircle(perfectCircle->GetChecked()); + }); + graveExitsConsole = addCheckbox(0, "Key under Esc exits console", "Disable if that key is 0 on your keyboard", [this] { + c->SetGraveExitsConsole(graveExitsConsole->GetChecked()); + }); + decoSpace = addDropDown("Colour space used by decoration tools", { + { "sRGB", 0 }, + { "Linear", 1 }, + { "Gamma 2.2", 2 }, + { "Gamma 1.8", 3 }, + }, [this] { + c->SetDecoSpace(decoSpace->GetOption().second); + }); - tempLabel = new ui::Label(ui::Point(scale->Position.X+scale->Size.X+3, currentY), ui::Point(Size.X-40, 16), "\bg- Window scale factor for larger screens"); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=20; - resizable = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Resizable", ""); - autowidth(resizable); - resizable->SetActionCallback({ [this] { c->SetResizable(resizable->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(resizable->Position.X+Graphics::textwidth(resizable->GetText())+20, currentY), ui::Point(1, 16), "\bg- Allow resizing and maximizing window"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(resizable); - - currentY+=20; - fullscreen = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Fullscreen", ""); - autowidth(fullscreen); - fullscreen->SetActionCallback({ [this] { c->SetFullscreen(fullscreen->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(fullscreen->Position.X+Graphics::textwidth(fullscreen->GetText())+20, currentY), ui::Point(1, 16), "\bg- Fill the entire screen"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(fullscreen); - - currentY+=20; - altFullscreen = new ui::Checkbox(ui::Point(23, currentY), ui::Point(1, 16), "Change Resolution", ""); - autowidth(altFullscreen); - altFullscreen->SetActionCallback({ [this] { c->SetAltFullscreen(altFullscreen->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(altFullscreen->Position.X+Graphics::textwidth(altFullscreen->GetText())+20, currentY), ui::Point(1, 16), "\bg- Set optimal screen resolution"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(altFullscreen); - - currentY+=20; - forceIntegerScaling = new ui::Checkbox(ui::Point(23, currentY), ui::Point(1, 16), "Force Integer Scaling", ""); - autowidth(forceIntegerScaling); - forceIntegerScaling->SetActionCallback({ [this] { c->SetForceIntegerScaling(forceIntegerScaling->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(altFullscreen->Position.X+Graphics::textwidth(forceIntegerScaling->GetText())+20, currentY), ui::Point(1, 16), "\bg- Less blurry"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(forceIntegerScaling); - - currentY+=20; - fastquit = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Fast Quit", ""); - autowidth(fastquit); - fastquit->SetActionCallback({ [this] { c->SetFastQuit(fastquit->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(fastquit->Position.X+Graphics::textwidth(fastquit->GetText())+20, currentY), ui::Point(1, 16), "\bg- Always exit completely when hitting close"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(fastquit); - - currentY+=20; - showAvatars = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Show Avatars", ""); - autowidth(showAvatars); - showAvatars->SetActionCallback({ [this] { c->SetShowAvatars(showAvatars->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(showAvatars->Position.X+Graphics::textwidth(showAvatars->GetText())+20, currentY), ui::Point(1, 16), "\bg- Disable if you have a slow connection"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(showAvatars); - - currentY += 20; - momentumScroll = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Momentum/Old Scrolling", ""); - autowidth(momentumScroll); - momentumScroll->SetActionCallback({ [this] { c->SetMomentumScroll(momentumScroll->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(momentumScroll->Position.X + Graphics::textwidth(momentumScroll->GetText()) + 20, currentY), ui::Point(1, 16), "\bg- Accelerating instead of step scroll"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(momentumScroll); - - currentY+=20; - mouseClickRequired = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Sticky Categories", ""); - autowidth(mouseClickRequired); - mouseClickRequired->SetActionCallback({ [this] { c->SetMouseClickrequired(mouseClickRequired->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(mouseClickRequired->Position.X+Graphics::textwidth(mouseClickRequired->GetText())+20, currentY), ui::Point(1, 16), "\bg- Switch between categories by clicking"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(mouseClickRequired); - - currentY+=20; - includePressure = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Include Pressure", ""); - autowidth(includePressure); - includePressure->SetActionCallback({ [this] { c->SetIncludePressure(includePressure->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(includePressure->Position.X+Graphics::textwidth(includePressure->GetText())+20, currentY), ui::Point(1, 16), "\bg- When saving, copying, stamping, etc."); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(includePressure); - - currentY+=20; - perfectCirclePressure = new ui::Checkbox(ui::Point(8, currentY), ui::Point(1, 16), "Perfect Circle", ""); - autowidth(perfectCirclePressure); - perfectCirclePressure->SetActionCallback({ [this] { c->SetPerfectCircle(perfectCirclePressure->GetChecked()); } }); - tempLabel = new ui::Label(ui::Point(perfectCirclePressure->Position.X+Graphics::textwidth(perfectCirclePressure->GetText())+20, currentY), ui::Point(1, 16), "\bg- Better circle brush, without incorrect points on edges"); - autowidth(tempLabel); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - scrollPanel->AddChild(perfectCirclePressure); - - currentY+=20; - decoSpace = new ui::DropDown(ui::Point(8, currentY), ui::Point(60, 16)); - decoSpace->SetActionCallback({ [this] { c->SetDecoSpace(decoSpace->GetOption().second); } }); - scrollPanel->AddChild(decoSpace); - decoSpace->AddOption(std::pair("sRGB", 0)); - decoSpace->AddOption(std::pair("Linear", 1)); - decoSpace->AddOption(std::pair("Gamma 2.2", 2)); - decoSpace->AddOption(std::pair("Gamma 1.8", 3)); - - tempLabel = new ui::Label(ui::Point(decoSpace->Position.X+decoSpace->Size.X+3, currentY), ui::Point(Size.X-40, 16), "\bg- Colour space used by decoration tools"); - tempLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; - tempLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; - scrollPanel->AddChild(tempLabel); - - currentY+=20; - ui::Button * dataFolderButton = new ui::Button(ui::Point(8, currentY), ui::Point(90, 16), "Open Data Folder"); - dataFolderButton->SetActionCallback({ [] { - ByteString cwd = Platform::GetCwd(); - if (!cwd.empty()) - Platform::OpenURI(cwd); - else - fprintf(stderr, "cannot open data folder: Platform::GetCwd(...) failed\n"); - } }); - scrollPanel->AddChild(dataFolderButton); - - ui::Button * migrationButton = new ui::Button(ui::Point(Size.X - 178, currentY), ui::Point(163, 16), "Migrate to shared data directory"); - migrationButton->SetActionCallback({ [] { - ByteString from = Platform::originalCwd; - ByteString to = Platform::sharedCwd; - new ConfirmPrompt("Do Migration?", "This will migrate all stamps, saves, and scripts from\n\bt" + from.FromUtf8() + "\bw\nto the shared data directory at\n\bt" + to.FromUtf8() + "\bw\n\n" + - "Files that already exist will not be overwritten.", { [=] () { - String ret = Platform::DoMigration(from, to); + { + currentY += 4; + auto *dataFolderButton = new ui::Button(ui::Point(10, currentY), ui::Point(90, 16), "Open data folder"); + dataFolderButton->SetActionCallback({ [] { + ByteString cwd = Platform::GetCwd(); + if (!cwd.empty()) + { + Platform::OpenURI(cwd); + } + else + { + std::cerr << "Cannot open data folder: Platform::GetCwd(...) failed" << std::endl; + } + } }); + scrollPanel->AddChild(dataFolderButton); + auto *migrationButton = new ui::Button(ui::Point(Size.X - 178, currentY), ui::Point(163, 16), "Migrate to shared data directory"); + migrationButton->SetActionCallback({ [] { + ByteString from = Platform::originalCwd; + ByteString to = Platform::sharedCwd; + new ConfirmPrompt("Do Migration?", "This will migrate all stamps, saves, and scripts from\n\bt" + from.FromUtf8() + "\bw\nto the shared data directory at\n\bt" + to.FromUtf8() + "\bw\n\n" + "Files that already exist will not be overwritten.", { [from, to]() { + String ret = Client::Ref().DoMigration(from, to); new InformationMessage("Migration Complete", ret, false); - } }); - } }); - scrollPanel->AddChild(migrationButton); - - ui::Button * tempButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK"); - tempButton->SetActionCallback({ [this] { c->Exit(); } }); - AddComponent(tempButton); - SetCancelButton(tempButton); - SetOkayButton(tempButton); - currentY+=20; + } }); + } }); + scrollPanel->AddChild(migrationButton); + currentY += 26; + } + { + ui::Button *ok = new ui::Button(ui::Point(0, Size.Y-16), ui::Point(Size.X, 16), "OK"); + ok->SetActionCallback({ [this] { + c->Exit(); + } }); + AddComponent(ok); + SetCancelButton(ok); + SetOkayButton(ok); + } scrollPanel->InnerSize = ui::Point(Size.X, currentY); } @@ -438,9 +337,7 @@ void OptionsView::UpdateAmbientAirTempPreview(float airTemp, bool isValid) { if (isValid) { - int HeatToColour(float temp); - int c = HeatToColour(airTemp); - ambientAirTempPreview->Appearance.BackgroundInactive = ui::Colour(PIXR(c), PIXG(c), PIXB(c)); + ambientAirTempPreview->Appearance.BackgroundInactive = RGB::Unpack(HeatToColour(airTemp)).WithAlpha(0xFF); ambientAirTempPreview->SetText(""); } else @@ -531,7 +428,8 @@ void OptionsView::NotifySettingsChanged(OptionsModel * sender) showAvatars->SetChecked(sender->GetShowAvatars()); mouseClickRequired->SetChecked(sender->GetMouseClickRequired()); includePressure->SetChecked(sender->GetIncludePressure()); - perfectCirclePressure->SetChecked(sender->GetPerfectCircle()); + perfectCircle->SetChecked(sender->GetPerfectCircle()); + graveExitsConsole->SetChecked(sender->GetGraveExitsConsole()); momentumScroll->SetChecked(sender->GetMomentumScroll()); } @@ -543,15 +441,11 @@ void OptionsView::AttachController(OptionsController * c_) void OptionsView::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); } void OptionsView::OnTryExit(ExitMethod method) { c->Exit(); } - - -OptionsView::~OptionsView() { -} diff --git a/src/gui/options/OptionsView.h b/src/gui/options/OptionsView.h index 5b519c4f2..ee9a6c1a4 100644 --- a/src/gui/options/OptionsView.h +++ b/src/gui/options/OptionsView.h @@ -1,6 +1,4 @@ -#ifndef OPTIONSVIEW_H_ -#define OPTIONSVIEW_H_ - +#pragma once #include "common/String.h" #include "gui/interface/Window.h" #include "gui/interface/ScrollPanel.h" @@ -39,7 +37,8 @@ class OptionsView: public ui::Window ui::Checkbox * momentumScroll; ui::Checkbox * mouseClickRequired; ui::Checkbox * includePressure; - ui::Checkbox * perfectCirclePressure; + ui::Checkbox * perfectCircle; + ui::Checkbox * graveExitsConsole; ui::ScrollPanel * scrollPanel; float customGravityX, customGravityY; void UpdateAmbientAirTempPreview(float airTemp, bool isValid); @@ -51,7 +50,4 @@ public: void AttachController(OptionsController * c_); void OnDraw() override; void OnTryExit(ExitMethod method) override; - virtual ~OptionsView(); }; - -#endif /* OPTIONSVIEW_H_ */ diff --git a/src/gui/preview/Comment.h b/src/gui/preview/Comment.h deleted file mode 100644 index 197a0da00..000000000 --- a/src/gui/preview/Comment.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef COMMENT_H_ -#define COMMENT_H_ - -#include "common/String.h" - -class SaveComment -{ -public: - int authorID; - ByteString authorName; - ByteString authorNameFormatted; - String comment; - SaveComment(int userID, ByteString username, ByteString usernameFormatted, String commentText): - authorID(userID), authorName(username), authorNameFormatted(usernameFormatted), comment(commentText) - { - } - SaveComment(const SaveComment & comment): - authorID(comment.authorID), authorName(comment.authorName), authorNameFormatted(comment.authorNameFormatted), comment(comment.comment) - { - } - SaveComment(const SaveComment * comment): - authorID(comment->authorID), authorName(comment->authorName), authorNameFormatted(comment->authorNameFormatted), comment(comment->comment) - { - } -}; - - -#endif /* COMMENT_H_ */ diff --git a/src/gui/preview/PreviewController.cpp b/src/gui/preview/PreviewController.cpp index 601597e2e..205c3f8b4 100644 --- a/src/gui/preview/PreviewController.cpp +++ b/src/gui/preview/PreviewController.cpp @@ -1,27 +1,30 @@ #include "PreviewController.h" - -#include "Config.h" #include "Controller.h" #include "PreviewModel.h" #include "PreviewModelException.h" #include "PreviewView.h" - #include "client/Client.h" #include "client/SaveInfo.h" -#include "common/Platform.h" - +#include "client/GameSave.h" +#include "client/http/GetSaveRequest.h" +#include "client/http/GetSaveDataRequest.h" +#include "client/http/GetCommentsRequest.h" +#include "client/http/FavouriteSaveRequest.h" +#include "common/platform/Platform.h" +#include "graphics/Graphics.h" #include "gui/dialogues/ErrorMessage.h" #include "gui/dialogues/InformationMessage.h" #include "gui/login/LoginController.h" #include "gui/login/LoginView.h" +#include "Config.h" -PreviewController::PreviewController(int saveID, int saveDate, bool instant, std::function onDone_): +PreviewController::PreviewController(int saveID, int saveDate, bool instant, std::function onDone_, std::unique_ptr thumbnail): saveId(saveID), loginWindow(NULL), HasExited(false) { previewModel = new PreviewModel(); - previewView = new PreviewView(); + previewView = new PreviewView(std::move(thumbnail)); previewModel->AddObserver(previewView); previewView->AttachController(this); previewModel->SetDoOpen(instant); @@ -52,30 +55,6 @@ void PreviewController::Update() } } -bool PreviewController::SubmitComment(String comment) -{ - if(comment.length() < 4) - { - new ErrorMessage("Error", "Comment is too short"); - return false; - } - else - { - RequestStatus status = Client::Ref().AddComment(saveId, comment); - if(status != RequestOkay) - { - new ErrorMessage("Error submitting comment", Client::Ref().GetLastError()); - return false; - } - else - { - previewModel->CommentAdded(); - previewModel->UpdateComments(1); - } - } - return true; -} - void PreviewController::ShowLogin() { loginWindow = new LoginController(); @@ -87,11 +66,16 @@ void PreviewController::NotifyAuthUserChanged(Client * sender) previewModel->SetCommentBoxEnabled(sender->GetAuthUser().UserID); } -SaveInfo * PreviewController::GetSaveInfo() +const SaveInfo *PreviewController::GetSaveInfo() const { return previewModel->GetSaveInfo(); } +std::unique_ptr PreviewController::TakeSaveInfo() +{ + return previewModel->TakeSaveInfo(); +} + bool PreviewController::GetDoOpen() { return previewModel->GetDoOpen(); @@ -102,32 +86,11 @@ void PreviewController::DoOpen() previewModel->SetDoOpen(true); } -void PreviewController::Report(String message) -{ - if(Client::Ref().ReportSave(saveId, message) == RequestOkay) - { - Exit(); - new InformationMessage("Information", "Report submitted", false); - } - else - new ErrorMessage("Error", "Unable to file report: " + Client::Ref().GetLastError()); -} - void PreviewController::FavouriteSave() { - if(previewModel->GetSaveInfo() && Client::Ref().GetAuthUser().UserID) + if (previewModel->GetSaveInfo() && Client::Ref().GetAuthUser().UserID) { - try - { - if(previewModel->GetSaveInfo()->Favourite) - previewModel->SetFavourite(false); - else - previewModel->SetFavourite(true); - } - catch (PreviewModelException & e) - { - new ErrorMessage("Error", ByteString(e.what()).FromUtf8()); - } + previewModel->SetFavourite(!previewModel->GetSaveInfo()->Favourite); } } @@ -157,6 +120,12 @@ bool PreviewController::PrevCommentPage() return false; } +void PreviewController::CommentAdded() +{ + previewModel->CommentAdded(); + previewModel->UpdateComments(1); +} + void PreviewController::Exit() { previewView->CloseActiveWindow(); @@ -167,8 +136,10 @@ void PreviewController::Exit() PreviewController::~PreviewController() { - previewView->CloseActiveWindow(); Client::Ref().RemoveListener(this); delete previewModel; - delete previewView; + if (previewView->CloseActiveWindow()) + { + delete previewView; + } } diff --git a/src/gui/preview/PreviewController.h b/src/gui/preview/PreviewController.h index 37707a5ee..834512807 100644 --- a/src/gui/preview/PreviewController.h +++ b/src/gui/preview/PreviewController.h @@ -1,11 +1,9 @@ -#ifndef PREVIEWCONTROLLER_H_ -#define PREVIEWCONTROLLER_H_ -#include "Config.h" - +#pragma once #include "client/ClientListener.h" - #include +#include +class VideoBuffer; class SaveInfo; class LoginController; class PreviewModel; @@ -21,23 +19,21 @@ public: inline int SaveID() { return saveId; } bool HasExited; - PreviewController(int saveID, int saveDate, bool instant, std::function onDone = nullptr); + PreviewController(int saveID, int saveDate, bool instant, std::function onDone, std::unique_ptr thumbnail); void Exit(); void DoOpen(); void OpenInBrowser(); - void Report(String message); void ShowLogin(); bool GetDoOpen(); - SaveInfo * GetSaveInfo(); + const SaveInfo *GetSaveInfo() const; + std::unique_ptr TakeSaveInfo(); PreviewView * GetView() { return previewView; } void Update(); void FavouriteSave(); - bool SubmitComment(String comment); bool NextCommentPage(); bool PrevCommentPage(); + void CommentAdded(); virtual ~PreviewController(); }; - -#endif /* PREVIEWCONTROLLER_H_ */ diff --git a/src/gui/preview/PreviewModel.cpp b/src/gui/preview/PreviewModel.cpp index 71d8d4529..2dd1d29ec 100644 --- a/src/gui/preview/PreviewModel.cpp +++ b/src/gui/preview/PreviewModel.cpp @@ -1,49 +1,27 @@ -#include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows - #include "PreviewModel.h" - -#include -#include - +#include "client/http/GetSaveDataRequest.h" +#include "client/http/GetSaveRequest.h" +#include "client/http/GetCommentsRequest.h" +#include "client/http/FavouriteSaveRequest.h" #include "Format.h" - +#include "Misc.h" #include "client/Client.h" #include "client/GameSave.h" #include "client/SaveInfo.h" - #include "gui/dialogues/ErrorMessage.h" -#include "gui/preview/Comment.h" - #include "PreviewModelException.h" #include "PreviewView.h" +#include "Config.h" +#include +#include -PreviewModel::PreviewModel(): - doOpen(false), - canOpen(true), - saveInfo(NULL), - saveData(NULL), - saveComments(NULL), - saveDataDownload(NULL), - commentsDownload(NULL), - commentBoxEnabled(false), - commentsLoaded(false), - commentsTotal(0), - commentsPageNumber(1) -{ - -} +constexpr auto commentsPerPage = 20; void PreviewModel::SetFavourite(bool favourite) { if (saveInfo) { - if (Client::Ref().FavouriteSave(saveInfo->id, favourite) == RequestOkay) - saveInfo->Favourite = favourite; - else if (favourite) - throw PreviewModelException("Error, could not fav. the save: " + Client::Ref().GetLastError()); - else - throw PreviewModelException("Error, could not unfav. the save: " + Client::Ref().GetLastError()); - notifySaveChanged(); + queuedFavourite = favourite; } } @@ -66,42 +44,23 @@ void PreviewModel::UpdateSave(int saveID, int saveDate) this->saveID = saveID; this->saveDate = saveDate; - if (saveInfo) - { - delete saveInfo; - saveInfo = NULL; - } - if (saveData) - { - delete saveData; - saveData = NULL; - } - ClearComments(); + saveInfo.reset(); + saveData.reset(); + saveComments.reset(); notifySaveChanged(); notifySaveCommentsChanged(); - ByteString url; - if (saveDate) - url = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, "_", saveDate, ".cps"); - else - url = ByteString::Build(STATICSCHEME, STATICSERVER, "/", saveID, ".cps"); - saveDataDownload = new http::Request(url); + saveDataDownload = std::make_unique(saveID, saveDate); saveDataDownload->Start(); - url = ByteString::Build(SCHEME, SERVER , "/Browse/View.json?ID=", saveID); - if (saveDate) - url += ByteString::Build("&Date=", saveDate); - saveInfoDownload = new http::Request(url); - saveInfoDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID); + saveInfoDownload = std::make_unique(saveID, saveDate); saveInfoDownload->Start(); if (!GetDoOpen()) { commentsLoaded = false; - url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", (commentsPageNumber-1)*20, "&Count=20"); - commentsDownload = new http::Request(url); - commentsDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID); + commentsDownload = std::make_unique(saveID, (commentsPageNumber - 1) * commentsPerPage, commentsPerPage); commentsDownload->Start(); } } @@ -121,9 +80,14 @@ bool PreviewModel::GetCanOpen() return canOpen; } -SaveInfo * PreviewModel::GetSaveInfo() +const SaveInfo *PreviewModel::GetSaveInfo() const { - return saveInfo; + return saveInfo.get(); +} + +std::unique_ptr PreviewModel::TakeSaveInfo() +{ + return std::move(saveInfo); } int PreviewModel::GetCommentsPageNum() @@ -133,7 +97,7 @@ int PreviewModel::GetCommentsPageNum() int PreviewModel::GetCommentsPageCount() { - return std::max(1, (int)(ceil(commentsTotal/20.0f))); + return std::max(1, ceilDiv(commentsTotal, commentsPerPage).first); } bool PreviewModel::GetCommentsLoaded() @@ -146,14 +110,12 @@ void PreviewModel::UpdateComments(int pageNumber) if (commentsLoaded) { commentsLoaded = false; - ClearComments(); + saveComments.reset(); commentsPageNumber = pageNumber; if (!GetDoOpen()) { - ByteString url = ByteString::Build(SCHEME, SERVER, "/Browse/Comments.json?ID=", saveID, "&Start=", (commentsPageNumber-1)*20, "&Count=20"); - commentsDownload = new http::Request(url); - commentsDownload->AuthHeaders(ByteString::Build(Client::Ref().GetAuthUser().UserID), Client::Ref().GetAuthUser().SessionID); + commentsDownload = std::make_unique(saveID, (commentsPageNumber - 1) * commentsPerPage, commentsPerPage); commentsDownload->Start(); } @@ -174,10 +136,10 @@ void PreviewModel::OnSaveReady() commentsTotal = saveInfo->Comments; try { - GameSave *gameSave = new GameSave(*saveData); + auto gameSave = std::make_unique(*saveData); if (gameSave->fromNewerVersion) new ErrorMessage("This save is from a newer version", "Please update TPT in game or at https://powdertoy.co.uk"); - saveInfo->SetGameSave(gameSave); + saveInfo->SetGameSave(std::move(gameSave)); } catch(ParseException &e) { @@ -191,180 +153,105 @@ void PreviewModel::OnSaveReady() notifySaveCommentsChanged(); } -void PreviewModel::ClearComments() -{ - if (saveComments) - { - for (size_t i = 0; i < saveComments->size(); i++) - delete saveComments->at(i); - saveComments->clear(); - delete saveComments; - saveComments = NULL; - } -} - -bool PreviewModel::ParseSaveInfo(ByteString &saveInfoResponse) -{ - delete saveInfo; - - try // how does this differ from Client::GetSave? - { - std::istringstream dataStream(saveInfoResponse); - Json::Value objDocument; - dataStream >> objDocument; - - int tempID = objDocument["ID"].asInt(); - int tempScoreUp = objDocument["ScoreUp"].asInt(); - int tempScoreDown = objDocument["ScoreDown"].asInt(); - int tempMyScore = objDocument["ScoreMine"].asInt(); - ByteString tempUsername = objDocument["Username"].asString(); - String tempName = ByteString(objDocument["Name"].asString()).FromUtf8(); - String tempDescription = ByteString(objDocument["Description"].asString()).FromUtf8(); - int tempCreatedDate = objDocument["DateCreated"].asInt(); - int tempUpdatedDate = objDocument["Date"].asInt(); - bool tempPublished = objDocument["Published"].asBool(); - bool tempFavourite = objDocument["Favourite"].asBool(); - int tempComments = objDocument["Comments"].asInt(); - int tempViews = objDocument["Views"].asInt(); - int tempVersion = objDocument["Version"].asInt(); - - Json::Value tagsArray = objDocument["Tags"]; - std::list tempTags; - for (Json::UInt j = 0; j < tagsArray.size(); j++) - tempTags.push_back(tagsArray[j].asString()); - - saveInfo = new SaveInfo(tempID, tempCreatedDate, tempUpdatedDate, tempScoreUp, - tempScoreDown, tempMyScore, tempUsername, tempName, - tempDescription, tempPublished, tempTags); - saveInfo->Comments = tempComments; - saveInfo->Favourite = tempFavourite; - saveInfo->Views = tempViews; - saveInfo->Version = tempVersion; - - // This is a workaround for a bug on the TPT server where the wrong 404 save is returned - // Redownload the .cps file for a fixed version of the 404 save - if (tempID == 404 && this->saveID != 404) - { - if (saveDataDownload) - saveDataDownload->Cancel(); - delete saveData; - saveData = NULL; - saveDataDownload = new http::Request(ByteString::Build(STATICSCHEME, STATICSERVER, "/2157797.cps")); - saveDataDownload->Start(); - } - return true; - } - catch (std::exception &e) - { - saveInfo = NULL; - return false; - } -} - -bool PreviewModel::ParseComments(ByteString &commentsResponse) -{ - ClearComments(); - saveComments = new std::vector(); - try - { - std::istringstream dataStream(commentsResponse); - Json::Value commentsArray; - dataStream >> commentsArray; - - for (Json::UInt j = 0; j < commentsArray.size(); j++) - { - int userID = ByteString(commentsArray[j]["UserID"].asString()).ToNumber(); - ByteString username = commentsArray[j]["Username"].asString(); - ByteString formattedUsername = commentsArray[j]["FormattedUsername"].asString(); - if (formattedUsername == "jacobot") - formattedUsername = "\bt" + formattedUsername; - String comment = ByteString(commentsArray[j]["Text"].asString()).FromUtf8(); - saveComments->push_back(new SaveComment(userID, username, formattedUsername, comment)); - } - return true; - } - catch (std::exception &e) - { - return false; - } -} - void PreviewModel::Update() { + auto triggerOnSaveReady = false; if (saveDataDownload && saveDataDownload->CheckDone()) { - int status; - ByteString ret = saveDataDownload->Finish(&status); - - ByteString nothing; - Client::Ref().ParseServerReturn(nothing, status, true); - if (status == 200 && ret.size()) + try { - delete saveData; - saveData = new std::vector(ret.begin(), ret.end()); - if (saveInfo && saveData) - OnSaveReady(); + saveData = saveDataDownload->Finish(); + triggerOnSaveReady = true; } - else + catch (const http::RequestError &ex) { + auto why = ByteString(ex.what()).FromUtf8(); for (size_t i = 0; i < observers.size(); i++) { - observers[i]->SaveLoadingError(Client::Ref().GetLastError()); + observers[i]->SaveLoadingError(why); } } - saveDataDownload = NULL; + saveDataDownload.reset(); } - if (saveInfoDownload && saveInfoDownload->CheckDone()) { - int status; - ByteString ret = saveInfoDownload->Finish(&status); - - ByteString nothing; - Client::Ref().ParseServerReturn(nothing, status, true); - if (status == 200 && ret.size()) + try { - if (ParseSaveInfo(ret)) + saveInfo = saveInfoDownload->Finish(); + triggerOnSaveReady = true; + // This is a workaround for a bug on the TPT server where the wrong 404 save is returned + // Redownload the .cps file for a fixed version of the 404 save + if (saveInfo->GetID() == 404 && saveID != 404) { - if (saveInfo && saveData) - OnSaveReady(); - } - else - { - for (size_t i = 0; i < observers.size(); i++) - observers[i]->SaveLoadingError("Could not parse save info"); + saveData.reset(); + saveDataDownload = std::make_unique(2157797, 0); + saveDataDownload->Start(); } } - else + catch (const http::RequestError &ex) { + auto why = ByteString(ex.what()).FromUtf8(); for (size_t i = 0; i < observers.size(); i++) - observers[i]->SaveLoadingError(Client::Ref().GetLastError()); + { + observers[i]->SaveLoadingError(why); + } } - saveInfoDownload = NULL; + saveInfoDownload.reset(); + } + if (triggerOnSaveReady && saveInfo && saveData) + { + OnSaveReady(); } if (commentsDownload && commentsDownload->CheckDone()) { - int status; - ByteString ret = commentsDownload->Finish(&status); - ClearComments(); - - ByteString nothing; - Client::Ref().ParseServerReturn(nothing, status, true); - if (status == 200 && ret.size()) - ParseComments(ret); - + try + { + saveComments = commentsDownload->Finish(); + } + catch (const http::RequestError &ex) + { + // TODO: handle + } commentsLoaded = true; notifySaveCommentsChanged(); notifyCommentsPageChanged(); - - commentsDownload = NULL; + commentsDownload.reset(); } -} -std::vector * PreviewModel::GetComments() -{ - return saveComments; + if (favouriteSaveRequest && favouriteSaveRequest->CheckDone()) + { + try + { + favouriteSaveRequest->Finish(); + if (saveInfo) + { + saveInfo->Favourite = favouriteSaveRequest->Favourite(); + notifySaveChanged(); + } + } + catch (const http::RequestError &ex) + { + if (favouriteSaveRequest->Favourite()) + { + throw PreviewModelException("Error, could not fav. the save: " + ByteString(ex.what()).FromUtf8()); + } + else + { + throw PreviewModelException("Error, could not unfav. the save: " + ByteString(ex.what()).FromUtf8()); + } + } + favouriteSaveRequest.reset(); + } + if (!favouriteSaveRequest && queuedFavourite) + { + if (saveInfo) + { + favouriteSaveRequest = std::make_unique(saveInfo->id, *queuedFavourite); + favouriteSaveRequest->Start(); + } + queuedFavourite.reset(); + } } void PreviewModel::notifySaveChanged() @@ -407,17 +294,3 @@ void PreviewModel::AddObserver(PreviewView * observer) observer->NotifyCommentsPageChanged(this); observer->NotifyCommentBoxEnabledChanged(this); } - - -PreviewModel::~PreviewModel() -{ - if (saveDataDownload) - saveDataDownload->Cancel(); - if (saveInfoDownload) - saveInfoDownload->Cancel(); - if (commentsDownload) - commentsDownload->Cancel(); - delete saveInfo; - delete saveData; - ClearComments(); -} diff --git a/src/gui/preview/PreviewModel.h b/src/gui/preview/PreviewModel.h index b512fddff..bbba16a90 100644 --- a/src/gui/preview/PreviewModel.h +++ b/src/gui/preview/PreviewModel.h @@ -1,46 +1,54 @@ -#ifndef PREVIEWMODEL_H -#define PREVIEWMODEL_H -#include "Config.h" - -#include +#pragma once #include "common/String.h" +#include "client/Comment.h" +#include +#include +#include namespace http { - class Request; + class GetSaveDataRequest; + class GetSaveRequest; + class GetCommentsRequest; + class FavouriteSaveRequest; } class PreviewView; class SaveInfo; -class SaveComment; class PreviewModel { - bool doOpen; - bool canOpen; + bool doOpen = false; + bool canOpen = true; std::vector observers; - SaveInfo * saveInfo; - std::vector * saveData; - std::vector * saveComments; + std::unique_ptr saveInfo; + std::optional> saveData; + std::optional> saveComments; void notifySaveChanged(); void notifySaveCommentsChanged(); void notifyCommentsPageChanged(); void notifyCommentBoxEnabledChanged(); - http::Request * saveDataDownload; - http::Request * saveInfoDownload; - http::Request * commentsDownload; + std::unique_ptr saveDataDownload; + std::unique_ptr saveInfoDownload; + std::unique_ptr commentsDownload; + std::unique_ptr favouriteSaveRequest; int saveID; int saveDate; - bool commentBoxEnabled; - bool commentsLoaded; - int commentsTotal; - int commentsPageNumber; + bool commentBoxEnabled = false; + bool commentsLoaded = false; + int commentsTotal = 0; + int commentsPageNumber = 1; + + std::optional queuedFavourite; public: - PreviewModel(); - SaveInfo * GetSaveInfo(); - std::vector * GetComments(); + const SaveInfo *GetSaveInfo() const; + std::unique_ptr TakeSaveInfo(); + const std::vector *GetComments() const + { + return saveComments ? &*saveComments : nullptr; + } bool GetCommentBoxEnabled(); void SetCommentBoxEnabled(bool enabledState); @@ -58,11 +66,7 @@ public: bool GetCanOpen(); void SetDoOpen(bool doOpen); void Update(); - void ClearComments(); void OnSaveReady(); bool ParseSaveInfo(ByteString &saveInfoResponse); bool ParseComments(ByteString &commentsResponse); - virtual ~PreviewModel(); }; - -#endif /* PREVIEWMODEL_H */ diff --git a/src/gui/preview/PreviewModelException.h b/src/gui/preview/PreviewModelException.h index 85191edab..551bf1732 100644 --- a/src/gui/preview/PreviewModelException.h +++ b/src/gui/preview/PreviewModelException.h @@ -1,6 +1,4 @@ -#ifndef PREVIEWMODELEXCEPTION_H_ -#define PREVIEWMODELEXCEPTION_H_ - +#pragma once #include "common/String.h" #include @@ -15,5 +13,3 @@ public: } ~PreviewModelException() throw() {} }; - -#endif /* PREVIEWMODELEXCEPTION_H_ */ diff --git a/src/gui/preview/PreviewView.cpp b/src/gui/preview/PreviewView.cpp index 195923495..e4b490451 100644 --- a/src/gui/preview/PreviewView.cpp +++ b/src/gui/preview/PreviewView.cpp @@ -4,6 +4,8 @@ #include "client/Client.h" #include "client/SaveInfo.h" +#include "client/http/AddCommentRequest.h" +#include "client/http/ReportSaveRequest.h" #include "gui/dialogues/TextPrompt.h" #include "gui/profile/ProfileActivity.h" @@ -12,28 +14,29 @@ #include "gui/preview/PreviewController.h" #include "gui/preview/PreviewModel.h" #include "gui/interface/Button.h" -#include "gui/interface/Keys.h" #include "gui/interface/CopyTextButton.h" #include "gui/interface/Label.h" #include "gui/interface/Textbox.h" #include "gui/interface/Engine.h" #include "gui/dialogues/ErrorMessage.h" +#include "gui/dialogues/InformationMessage.h" #include "gui/interface/Point.h" #include "gui/interface/Window.h" #include "gui/Style.h" #include "common/tpt-rand.h" -#include "Comment.h" #include "Format.h" #include "Misc.h" +#include "SimulationConfig.h" +#include + #ifdef GetUserName # undef GetUserName // dammit windows #endif -PreviewView::PreviewView(): +PreviewView::PreviewView(std::unique_ptr newSavePreview): ui::Window(ui::Point(-1, -1), ui::Point((XRES/2)+210, (YRES/2)+150)), - savePreview(NULL), submitCommentButton(NULL), addCommentBox(NULL), commentWarningLabel(NULL), @@ -46,11 +49,17 @@ PreviewView::PreviewView(): commentBoxHeight(20), commentHelpText(false) { + if (newSavePreview) + { + newSavePreview->Resize(RES / 2, true); + savePreview = std::move(newSavePreview); + } showAvatars = ui::Engine::Ref().ShowAvatars; favButton = new ui::Button(ui::Point(50, Size.Y-19), ui::Point(51, 19), "Fav"); favButton->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; favButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; + favButton->SetTogglable(true); favButton->SetIcon(IconFavourite); favButton->SetActionCallback({ [this] { c->FavouriteSave(); } }); favButton->Enabled = Client::Ref().GetAuthUser().UserID?true:false; @@ -62,7 +71,12 @@ PreviewView::PreviewView(): reportButton->SetIcon(IconReport); reportButton->SetActionCallback({ [this] { new TextPrompt("Report Save", "Things to consider when reporting:\n\bw1)\bg When reporting stolen saves, please include the ID of the original save.\n\bw2)\bg Do not ask for saves to be removed from front page unless they break the rules.\n\bw3)\bg You may report saves for comments or tags too (including your own saves)", "", "[reason]", true, { [this](String const &resultText) { - c->Report(resultText); + if (reportSaveRequest) + { + return; + } + reportSaveRequest = std::make_unique(c->SaveID(), resultText); + reportSaveRequest->Start(); } }); } }); reportButton->Enabled = Client::Ref().GetAuthUser().UserID?true:false; @@ -147,13 +161,13 @@ void PreviewView::AttachController(PreviewController * controller) { c = controller; - int textWidth = Graphics::textwidth("Click the box below to copy the save ID"); + int textWidth = Graphics::TextSize("Click the box below to copy the save ID").X - 1; saveIDLabel = new ui::Label(ui::Point((Size.X-textWidth-20)/2, Size.Y+5), ui::Point(textWidth+20, 16), "Click the box below to copy the save ID"); saveIDLabel->SetTextColour(ui::Colour(150, 150, 150)); saveIDLabel->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; AddComponent(saveIDLabel); - textWidth = Graphics::textwidth(String::Build(c->SaveID())); + textWidth = Graphics::TextSize(String::Build(c->SaveID())).X - 1; saveIDLabel2 = new ui::Label(ui::Point((Size.X-textWidth-20)/2-37, Size.Y+22), ui::Point(40, 16), "Save ID:"); AddComponent(saveIDLabel2); @@ -165,7 +179,7 @@ void PreviewView::commentBoxAutoHeight() { if(!addCommentBox) return; - int textWidth = Graphics::textwidth(addCommentBox->GetText().c_str()); + int textWidth = Graphics::TextSize(addCommentBox->GetText().c_str()).X - 1; if (commentHelpText || textWidth+15 > Size.X-(XRES/2)-48) { addCommentBox->Appearance.VerticalAlign = ui::Appearance::AlignTop; @@ -216,11 +230,16 @@ void PreviewView::CheckComment() if (!commentWarningLabel) return; String text = addCommentBox->GetText().ToLower(); - if (!userIsAuthor && (text.Contains("stolen") || text.Contains("copied"))) + if (addCommentRequest) + { + commentWarningLabel->SetText("Submitting comment..."); + commentHelpText = true; + } + else if (!userIsAuthor && (text.Contains("stolen") || text.Contains("copied"))) { if (!commentHelpText) { - if (random_gen()%2) + if (interfaceRng()%2) commentWarningLabel->SetText("Stolen? Report the save instead"); else commentWarningLabel->SetText("Please report stolen saves"); @@ -236,7 +255,7 @@ void PreviewView::CheckComment() { if (!commentHelpText) { - if (random_gen()%2) + if (interfaceRng()%2) commentWarningLabel->SetText("Please do not swear"); else commentWarningLabel->SetText("Bad language may be deleted"); @@ -258,20 +277,18 @@ void PreviewView::DoDraw() { int linePos = commentTextComponents[i]->Position.Y+commentsPanel->ViewportPosition.Y+commentTextComponents[i]->Size.Y+4; if (linePos > 0 && linePos < Size.Y-commentBoxHeight) - g->draw_line( - Position.X+1+XRES/2, - Position.Y+linePos, - Position.X+Size.X-2, - Position.Y+linePos, - 255, 255, 255, 100); + g->BlendLine( + Position + Vec2{ 1+XRES/2, linePos }, + Position + Vec2{ Size.X-2, linePos }, + 0xFFFFFF_rgb .WithAlpha(100)); } if (c->GetDoOpen()) { - g->fillrect(Position.X+(Size.X/2)-101, Position.Y+(Size.Y/2)-26, 202, 52, 0, 0, 0, 210); - g->drawrect(Position.X+(Size.X/2)-100, Position.Y+(Size.Y/2)-25, 200, 50, 255, 255, 255, 180); - g->drawtext(Position.X+(Size.X/2)-(Graphics::textwidth("Loading save...")/2), Position.Y+(Size.Y/2)-5, "Loading save...", style::Colour::InformationTitle.Red, style::Colour::InformationTitle.Green, style::Colour::InformationTitle.Blue, 255); + g->BlendFilledRect(RectSized(Position + Size / 2 - Vec2{ 101, 26 }, { 202, 52 }), 0x000000_rgb .WithAlpha(210)); + g->BlendRect(RectSized(Position + Size / 2 - Vec2{ 100, 25 }, Vec2{ 200, 50 }), 0xFFFFFF_rgb .WithAlpha(180)); + g->BlendText(Position + Vec2{(Size.X/2)-((Graphics::TextSize("Loading save...").X - 1)/2), (Size.Y/2)-5}, "Loading save...", style::Colour::InformationTitle.NoAlpha().WithAlpha(255)); } - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); } @@ -280,15 +297,15 @@ void PreviewView::OnDraw() Graphics * g = GetGraphics(); //Window Background+Outline - g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); //Save preview (top-left) - if(savePreview && savePreview->Buffer) + if (savePreview) { - g->draw_image(savePreview, (Position.X+1)+(((XRES/2)-savePreview->Width)/2), (Position.Y+1)+(((YRES/2)-savePreview->Height)/2), 255); + g->BlendImage(savePreview->Data(), 0xFF, RectSized(Position + Vec2(1, 1) + (RES / 2 - savePreview->Size()) / 2, savePreview->Size())); } - g->drawrect(Position.X, Position.Y, (XRES/2)+1, (YRES/2)+1, 255, 255, 255, 100); - g->draw_line(Position.X+XRES/2, Position.Y+1, Position.X+XRES/2, Position.Y+Size.Y-2, 200, 200, 200, 255); + g->BlendRect(RectSized(Position, RES / 2 + Vec2{ 1, 1 }), 0xFFFFFF_rgb .WithAlpha(100)); + g->DrawLine(Position + Vec2{ XRES/2, 1 }, Position + Vec2{ XRES/2, Size.Y-2 }, 0xC8C8C8_rgb); if(votesUp || votesDown) { @@ -312,13 +329,13 @@ void PreviewView::OnDraw() nyu = nyu>50?50:nyu; nyd = nyd>50?50:nyd; - g->fillrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+3, 53, 7, 0, 107, 10, 255); - g->fillrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+9, 53, 7, 107, 10, 0, 255); - g->drawrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+3, 53, 7, 128, 128, 128, 255); - g->drawrect(Position.X+(XRES/2)-55, Position.Y+(YRES/2)+9, 53, 7, 128, 128, 128, 255); + g->DrawFilledRect(RectSized(Position + RES / 2 + Vec2{ -56, 3 }, Vec2{ 54, 7 }), 0x006B0A_rgb); + g->DrawFilledRect(RectSized(Position + RES / 2 + Vec2{ -56, 9 }, Vec2{ 54, 7 }), 0x6B0A00_rgb); + g->DrawRect(RectSized(Position + Vec2{ (XRES/2)-56, (YRES/2)+3 }, { 54, 7 }), 0x808080_rgb); + g->DrawRect(RectSized(Position + Vec2{ (XRES/2)-56, (YRES/2)+9 }, { 54, 7 }), 0x808080_rgb); - g->fillrect(Position.X+(XRES/2)-4-nyu, Position.Y+(YRES/2)+5, nyu, 3, 57, 187, 57, 255); - g->fillrect(Position.X+(XRES/2)-4-nyd, Position.Y+(YRES/2)+11, nyd, 3, 187, 57, 57, 255); + g->DrawFilledRect(RectSized(Position + RES / 2 + Vec2{ -4-nyu, 5 }, Vec2{ nyu, 3 }), 0x39BB39_rgb); + g->DrawFilledRect(RectSized(Position + RES / 2 + Vec2{ -4-nyd, 11 }, Vec2{ nyd, 3 }), 0xBB3939_rgb); } } @@ -371,6 +388,37 @@ void PreviewView::OnTick(float dt) ErrorMessage::Blocking("Error loading save", doErrorMessage); c->Exit(); } + + if (reportSaveRequest && reportSaveRequest->CheckDone()) + { + try + { + reportSaveRequest->Finish(); + c->Exit(); + new InformationMessage("Information", "Report submitted", false); + } + catch (const http::RequestError &ex) + { + new ErrorMessage("Error", "Unable to file report: " + ByteString(ex.what()).FromUtf8()); + } + reportSaveRequest.reset(); + } + if (addCommentRequest && addCommentRequest->CheckDone()) + { + try + { + addCommentBox->SetText(""); + c->CommentAdded(); + } + catch (const http::RequestError &ex) + { + new ErrorMessage("Error submitting comment", ByteString(ex.what()).FromUtf8()); + } + submitCommentButton->Enabled = true; + commentBoxAutoHeight(); + addCommentRequest.reset(); + CheckComment(); + } } void PreviewView::OnTryExit(ExitMethod method) @@ -415,9 +463,7 @@ void PreviewView::OnKeyPress(int key, int scan, bool repeat, bool shift, bool ct void PreviewView::NotifySaveChanged(PreviewModel * sender) { - SaveInfo * save = sender->GetSaveInfo(); - delete savePreview; - savePreview = NULL; + auto *save = sender->GetSaveInfo(); if(save) { votesUp = save->votesUp; @@ -446,34 +492,24 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender) if(save->Favourite) { favButton->Enabled = true; - favButton->SetText("Unfav"); + favButton->SetToggleState(true); } else if(Client::Ref().GetAuthUser().UserID) { favButton->Enabled = true; - favButton->SetText("Fav"); + favButton->SetToggleState(false); } else { - favButton->SetText("Fav"); + favButton->SetToggleState(false); favButton->Enabled = false; } if(save->GetGameSave()) { savePreview = SaveRenderer::Ref().Render(save->GetGameSave(), false, true); - - if(savePreview && savePreview->Buffer && !(savePreview->Width == XRES/2 && savePreview->Height == YRES/2)) - { - pixel * oldData = savePreview->Buffer; - float factorX = ((float)XRES/2)/((float)savePreview->Width); - float factorY = ((float)YRES/2)/((float)savePreview->Height); - float scaleFactor = factorY < factorX ? factorY : factorX; - savePreview->Buffer = Graphics::resample_img(oldData, savePreview->Width, savePreview->Height, int(savePreview->Width*scaleFactor), int(savePreview->Height*scaleFactor)); - delete[] oldData; - savePreview->Width = int(savePreview->Width * scaleFactor); - savePreview->Height = int(savePreview->Height * scaleFactor); - } + if (savePreview) + savePreview->ResizeToFit(RES / 2, true); } else if (!sender->GetCanOpen()) openButton->Enabled = false; @@ -485,6 +521,7 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender) saveNameLabel->SetText(""); authorDateLabel->SetText(""); saveDescriptionLabel->SetText(""); + favButton->SetToggleState(false); favButton->Enabled = false; if (!sender->GetCanOpen()) openButton->Enabled = false; @@ -493,21 +530,22 @@ void PreviewView::NotifySaveChanged(PreviewModel * sender) void PreviewView::submitComment() { - if(addCommentBox) + if (addCommentBox) { String comment = addCommentBox->GetText(); + if (comment.length() < 4) + { + new ErrorMessage("Error", "Comment is too short"); + return; + } + submitCommentButton->Enabled = false; - addCommentBox->SetText(""); - addCommentBox->SetPlaceholder("Submitting comment"); //This doesn't appear to ever show since no separate thread is created FocusComponent(NULL); - if (!c->SubmitComment(comment)) - addCommentBox->SetText(comment); + addCommentRequest = std::make_unique(c->SaveID(), comment); + addCommentRequest->Start(); - addCommentBox->SetPlaceholder("Add comment"); - submitCommentButton->Enabled = true; - - commentBoxAutoHeight(); + CheckComment(); } } @@ -572,7 +610,7 @@ void PreviewView::NotifyCommentsPageChanged(PreviewModel * sender) void PreviewView::NotifyCommentsChanged(PreviewModel * sender) { - std::vector * comments = sender->GetComments(); + auto commentsPtr = sender->GetComments(); for (size_t i = 0; i < commentComponents.size(); i++) { @@ -583,8 +621,9 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender) commentTextComponents.clear(); commentsPanel->InnerSize = ui::Point(0, 0); - if (comments) + if (commentsPtr) { + auto &comments = *commentsPtr; for (size_t i = 0; i < commentComponents.size(); i++) { commentsPanel->RemoveChild(commentComponents[i]); @@ -597,11 +636,11 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender) ui::Label * tempUsername; ui::Label * tempComment; ui::AvatarButton * tempAvatar; - for (size_t i = 0; i < comments->size(); i++) + for (size_t i = 0; i < comments.size(); i++) { if (showAvatars) { - tempAvatar = new ui::AvatarButton(ui::Point(2, currentY+7), ui::Point(26, 26), comments->at(i)->authorName); + tempAvatar = new ui::AvatarButton(ui::Point(2, currentY+7), ui::Point(26, 26), comments[i].authorName); tempAvatar->SetActionCallback({ [tempAvatar] { if (tempAvatar->GetUsername().size() > 0) { @@ -612,25 +651,38 @@ void PreviewView::NotifyCommentsChanged(PreviewModel * sender) commentsPanel->AddChild(tempAvatar); } + auto authorNameFormatted = comments[i].authorName.FromUtf8(); + if (comments[i].authorElevation != User::ElevationNone || comments[i].authorName == "jacobot") + { + authorNameFormatted = "\bt" + authorNameFormatted; + } + else if (comments[i].authorIsBanned) + { + authorNameFormatted = "\bg" + authorNameFormatted; + } + else if (Client::Ref().GetAuthUser().UserID && Client::Ref().GetAuthUser().Username == comments[i].authorName) + { + authorNameFormatted = "\bo" + authorNameFormatted; + } + else if (sender->GetSaveInfo() && sender->GetSaveInfo()->GetUserName() == comments[i].authorName) + { + authorNameFormatted = "\bl" + authorNameFormatted; + } if (showAvatars) - tempUsername = new ui::Label(ui::Point(31, currentY+3), ui::Point(Size.X-((XRES/2) + 13 + 26), 16), comments->at(i)->authorNameFormatted.FromUtf8()); + tempUsername = new ui::Label(ui::Point(31, currentY+3), ui::Point(Size.X-((XRES/2) + 13 + 26), 16), authorNameFormatted); else - tempUsername = new ui::Label(ui::Point(5, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), comments->at(i)->authorNameFormatted.FromUtf8()); + tempUsername = new ui::Label(ui::Point(5, currentY+3), ui::Point(Size.X-((XRES/2) + 13), 16), authorNameFormatted); tempUsername->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempUsername->Appearance.VerticalAlign = ui::Appearance::AlignBottom; - if (Client::Ref().GetAuthUser().UserID && Client::Ref().GetAuthUser().Username == comments->at(i)->authorName) - tempUsername->SetTextColour(ui::Colour(255, 255, 100)); - else if (sender->GetSaveInfo() && sender->GetSaveInfo()->GetUserName() == comments->at(i)->authorName) - tempUsername->SetTextColour(ui::Colour(255, 100, 100)); currentY += 16; commentComponents.push_back(tempUsername); commentsPanel->AddChild(tempUsername); if (showAvatars) - tempComment = new ui::Label(ui::Point(31, currentY+5), ui::Point(Size.X-((XRES/2) + 13 + 26), -1), comments->at(i)->comment); + tempComment = new ui::Label(ui::Point(31, currentY+5), ui::Point(Size.X-((XRES/2) + 13 + 26), -1), comments[i].content); else - tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), -1), comments->at(i)->comment); + tempComment = new ui::Label(ui::Point(5, currentY+5), ui::Point(Size.X-((XRES/2) + 13), -1), comments[i].content); tempComment->SetMultiline(true); tempComment->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; tempComment->Appearance.VerticalAlign = ui::Appearance::AlignTop; @@ -665,5 +717,4 @@ PreviewView::~PreviewView() RemoveComponent(submitCommentButton); delete submitCommentButton; } - delete savePreview; } diff --git a/src/gui/preview/PreviewView.h b/src/gui/preview/PreviewView.h index fc373d094..02deafd4f 100644 --- a/src/gui/preview/PreviewView.h +++ b/src/gui/preview/PreviewView.h @@ -1,11 +1,16 @@ -#ifndef PREVIEWVIEW_H_ -#define PREVIEWVIEW_H_ - -#include +#pragma once +#include #include +#include #include "common/String.h" #include "gui/interface/Window.h" +namespace http +{ + class AddCommentRequest; + class ReportSaveRequest; +} + namespace ui { class Button; @@ -22,7 +27,7 @@ class PreviewController; class PreviewView: public ui::Window { PreviewController * c; - VideoBuffer * savePreview; + std::unique_ptr savePreview; ui::Button * openButton; ui::Button * browserOpenButton; ui::Button * favButton; @@ -65,9 +70,13 @@ class PreviewView: public ui::Window void submitComment(); bool CheckSwearing(String text); void CheckComment(); + + std::unique_ptr addCommentRequest; + std::unique_ptr reportSaveRequest; + public: void AttachController(PreviewController * controller); - PreviewView(); + PreviewView(std::unique_ptr newSavePreviev); void NotifySaveChanged(PreviewModel * sender); void NotifyCommentsChanged(PreviewModel * sender); void NotifyCommentsPageChanged(PreviewModel * sender); @@ -82,5 +91,3 @@ public: void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override; virtual ~PreviewView(); }; - -#endif /* PREVIEWVIEW_H_ */ diff --git a/src/gui/profile/ProfileActivity.cpp b/src/gui/profile/ProfileActivity.cpp index 9503184da..fc472ae7f 100644 --- a/src/gui/profile/ProfileActivity.cpp +++ b/src/gui/profile/ProfileActivity.cpp @@ -1,15 +1,16 @@ #include "ProfileActivity.h" - #include "client/Client.h" -#include "common/Platform.h" +#include "client/http/SaveUserInfoRequest.h" +#include "client/http/GetUserInfoRequest.h" +#include "common/platform/Platform.h" #include "gui/Style.h" - #include "gui/interface/AvatarButton.h" #include "gui/interface/Button.h" #include "gui/dialogues/ErrorMessage.h" #include "gui/interface/Label.h" #include "gui/interface/ScrollPanel.h" #include "gui/interface/Textbox.h" +#include "Config.h" ProfileActivity::ProfileActivity(ByteString username) : WindowActivity(ui::Point(-1, -1), ui::Point(236, 300)), @@ -37,8 +38,8 @@ ProfileActivity::ProfileActivity(ByteString username) : saving = true; info.location = location->GetText(); info.biography = bio->GetText(); - SaveUserInfoRequestMonitor::RequestSetup(info); - SaveUserInfoRequestMonitor::RequestStart(); + saveUserInfoRequest = std::make_unique(info); + saveUserInfoRequest->Start(); } } }); AddComponent(saveButton); @@ -48,8 +49,8 @@ ProfileActivity::ProfileActivity(ByteString username) : loading = true; - GetUserInfoRequestMonitor::RequestSetup(username); - GetUserInfoRequestMonitor::RequestStart(); + getUserInfoRequest = std::make_unique(username); + getUserInfoRequest->Start(); } void ProfileActivity::setUserInfo(UserInfo newInfo) @@ -82,7 +83,7 @@ void ProfileActivity::setUserInfo(UserInfo newInfo) { ui::Button * editAvatar = new ui::Button(ui::Point(Size.X - (40 + 16 + 75), currentY), ui::Point(75, 15), "Edit Avatar"); editAvatar->SetActionCallback({ [] { - Platform::OpenURI(SCHEME SERVER "/Profile/Avatar.html"); + Platform::OpenURI(ByteString::Build(SCHEME, SERVER, "/Profile/Avatar.html")); } }); scrollPanel->AddChild(editAvatar); } @@ -191,33 +192,6 @@ void ProfileActivity::setUserInfo(UserInfo newInfo) scrollPanel->InnerSize = ui::Point(Size.X, currentY); } -void ProfileActivity::OnResponse(bool SaveUserInfoStatus) -{ - if (SaveUserInfoStatus) - { - Exit(); - } - else - { - doError = true; - doErrorMessage = "Could not save user info: " + Client::Ref().GetLastError(); - } -} - -void ProfileActivity::OnResponse(std::unique_ptr getUserInfoResult) -{ - if (getUserInfoResult) - { - loading = false; - setUserInfo(*getUserInfoResult); - } - else - { - doError = true; - doErrorMessage = "Could not load user info: " + Client::Ref().GetLastError(); - } -} - void ProfileActivity::OnTick(float dt) { if (doError) @@ -226,15 +200,42 @@ void ProfileActivity::OnTick(float dt) Exit(); } - SaveUserInfoRequestMonitor::RequestPoll(); - GetUserInfoRequestMonitor::RequestPoll(); + if (saveUserInfoRequest && saveUserInfoRequest->CheckDone()) + { + try + { + saveUserInfoRequest->Finish(); + Exit(); + } + catch (const http::RequestError &ex) + { + doError = true; + doErrorMessage = "Could not save user info: " + ByteString(ex.what()).FromUtf8(); + } + saveUserInfoRequest.reset(); + } + if (getUserInfoRequest && getUserInfoRequest->CheckDone()) + { + try + { + auto userInfo = getUserInfoRequest->Finish(); + loading = false; + setUserInfo(userInfo); + } + catch (const http::RequestError &ex) + { + doError = true; + doErrorMessage = "Could not load user info: " + ByteString(ex.what()).FromUtf8(); + } + getUserInfoRequest.reset(); + } } void ProfileActivity::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); } void ProfileActivity::OnTryExit(ExitMethod method) diff --git a/src/gui/profile/ProfileActivity.h b/src/gui/profile/ProfileActivity.h index 0498588b6..87db26142 100644 --- a/src/gui/profile/ProfileActivity.h +++ b/src/gui/profile/ProfileActivity.h @@ -1,21 +1,21 @@ -#ifndef PROFILEACTIVITY_H_ -#define PROFILEACTIVITY_H_ - +#pragma once #include "common/String.h" #include "Activity.h" #include "client/UserInfo.h" -#include "client/http/SaveUserInfoRequest.h" -#include "client/http/GetUserInfoRequest.h" -#include "client/http/RequestMonitor.h" +#include + +namespace http +{ + class SaveUserInfoRequest; + class GetUserInfoRequest; +} namespace ui { class Label; class ScrollPanel; } -using SaveUserInfoRequestMonitor = http::RequestMonitor; -using GetUserInfoRequestMonitor = http::RequestMonitor; -class ProfileActivity: public WindowActivity, public SaveUserInfoRequestMonitor, public GetUserInfoRequestMonitor { +class ProfileActivity: public WindowActivity { ui::ScrollPanel *scrollPanel; ui::Label *location; ui::Label *bio; @@ -26,6 +26,10 @@ class ProfileActivity: public WindowActivity, public SaveUserInfoRequestMonitor, bool doError; String doErrorMessage; void setUserInfo(UserInfo newInfo); + + std::unique_ptr saveUserInfoRequest; + std::unique_ptr getUserInfoRequest; + public: ProfileActivity(ByteString username); virtual ~ProfileActivity(); @@ -33,10 +37,5 @@ public: void OnDraw() override; void OnTryExit(ExitMethod method) override; - void OnResponse(bool saveUserInfoStatus) override; - void OnResponse(std::unique_ptr getUserInfoResult) override; - void ResizeArea(); }; - -#endif /* PROFILEACTIVITY_H_ */ diff --git a/src/gui/render/RenderController.cpp b/src/gui/render/RenderController.cpp index 4c5b4c04b..9c2842dbe 100644 --- a/src/gui/render/RenderController.cpp +++ b/src/gui/render/RenderController.cpp @@ -58,8 +58,10 @@ void RenderController::Exit() RenderController::~RenderController() { - renderView->CloseActiveWindow(); delete renderModel; - delete renderView; + if (renderView->CloseActiveWindow()) + { + delete renderView; + } } diff --git a/src/gui/render/RenderController.h b/src/gui/render/RenderController.h index 5d16642b6..06ab6dc3d 100644 --- a/src/gui/render/RenderController.h +++ b/src/gui/render/RenderController.h @@ -1,7 +1,4 @@ -#ifndef RENDERCONTROLLER_H_ -#define RENDERCONTROLLER_H_ -#include "Config.h" - +#pragma once #include class RenderView; @@ -25,5 +22,3 @@ public: void SetColourMode(unsigned int renderMode); void LoadRenderPreset(int presetNum); }; - -#endif /* RENDERCONTROLLER_H_ */ diff --git a/src/gui/render/RenderModel.h b/src/gui/render/RenderModel.h index b46440e25..ca41c2e9e 100644 --- a/src/gui/render/RenderModel.h +++ b/src/gui/render/RenderModel.h @@ -1,7 +1,4 @@ -#ifndef RENDERMODEL_H_ -#define RENDERMODEL_H_ -#include "Config.h" - +#pragma once #include class RenderView; @@ -30,5 +27,3 @@ public: void LoadRenderPreset(int presetNum); virtual ~RenderModel(); }; - -#endif /* RENDERMODEL_H_ */ diff --git a/src/gui/render/RenderView.cpp b/src/gui/render/RenderView.cpp index 93e68db88..4023ecdf1 100644 --- a/src/gui/render/RenderView.cpp +++ b/src/gui/render/RenderView.cpp @@ -155,22 +155,22 @@ void RenderView::NotifyColourChanged(RenderModel * sender) void RenderView::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(-1, -1, WINDOWW+1, WINDOWH+1); + g->DrawFilledRect(WINDOW.OriginRect(), 0x000000_rgb); if(ren) { - ren->clearScreen(1.0f); + ren->clearScreen(); ren->RenderBegin(); ren->RenderEnd(); } - g->draw_line(0, YRES, XRES-1, YRES, 200, 200, 200, 255); - g->draw_line(line1, YRES, line1, WINDOWH, 200, 200, 200, 255); - g->draw_line(line2, YRES, line2, WINDOWH, 200, 200, 200, 255); - g->draw_line(line3, YRES, line3, WINDOWH, 200, 200, 200, 255); - g->draw_line(line4, YRES, line4, WINDOWH, 200, 200, 200, 255); - g->draw_line(XRES, 0, XRES, WINDOWH, 255, 255, 255, 255); + g->DrawLine({ 0, YRES }, { XRES-1, YRES }, 0xC8C8C8_rgb); + g->DrawLine({ line1, YRES }, { line1, WINDOWH }, 0xC8C8C8_rgb); + g->DrawLine({ line2, YRES }, { line2, WINDOWH }, 0xC8C8C8_rgb); + g->DrawLine({ line3, YRES }, { line3, WINDOWH }, 0xC8C8C8_rgb); + g->DrawLine({ line4, YRES }, { line4, WINDOWH }, 0xC8C8C8_rgb); + g->DrawLine({ XRES, 0 }, { XRES, WINDOWH }, 0xFFFFFF_rgb); if(toolTipPresence && toolTip.length()) { - g->drawtext(6, Size.Y-MENUSIZE-12, toolTip, 255, 255, 255, toolTipPresence>51?255:toolTipPresence*5); + g->BlendText({ 6, Size.Y-MENUSIZE-12 }, toolTip, 0xFFFFFF_rgb .WithAlpha(toolTipPresence>51?255:toolTipPresence*5)); } } diff --git a/src/gui/render/RenderView.h b/src/gui/render/RenderView.h index 0c2cb92fb..fb7478dec 100644 --- a/src/gui/render/RenderView.h +++ b/src/gui/render/RenderView.h @@ -1,8 +1,6 @@ -#ifndef RENDERVIEW_H_ -#define RENDERVIEW_H_ - -#include +#pragma once #include "gui/interface/Window.h" +#include class ModeCheckbox; @@ -34,5 +32,3 @@ public: void ToolTip(ui::Point senderPosition, String toolTip) override; virtual ~RenderView(); }; - -#endif /* RENDERVIEW_H_ */ diff --git a/src/gui/save/LocalSaveActivity.cpp b/src/gui/save/LocalSaveActivity.cpp index ee58da66d..46ece246e 100644 --- a/src/gui/save/LocalSaveActivity.cpp +++ b/src/gui/save/LocalSaveActivity.cpp @@ -3,7 +3,7 @@ #include "client/Client.h" #include "client/GameSave.h" #include "client/ThumbnailRendererTask.h" -#include "common/Platform.h" +#include "common/platform/Platform.h" #include "graphics/Graphics.h" #include "gui/Style.h" @@ -13,23 +13,21 @@ #include "gui/interface/Label.h" #include "gui/interface/Textbox.h" -#include "save_local.png.h" +#include "Config.h" -LocalSaveActivity::LocalSaveActivity(SaveFile save, OnSaved onSaved_) : +LocalSaveActivity::LocalSaveActivity(std::unique_ptr newSave, OnSaved onSaved_) : WindowActivity(ui::Point(-1, -1), ui::Point(220, 200)), - save(save), + save(std::move(newSave)), thumbnailRenderer(nullptr), onSaved(onSaved_) { - PngDataToPixels(save_to_disk_image, save_to_disk_imageW, save_to_disk_imageH, reinterpret_cast(save_local_png), save_local_png_size, false); - ui::Label * titleLabel = new ui::Label(ui::Point(4, 5), ui::Point(Size.X-8, 16), "Save to computer:"); titleLabel->SetTextColour(style::Colour::InformationTitle); titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; AddComponent(titleLabel); - filenameField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), save.GetDisplayName(), "[filename]"); + filenameField = new ui::Textbox(ui::Point(8, 25), ui::Point(Size.X-16, 16), save->GetDisplayName(), "[filename]"); filenameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; filenameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; AddComponent(filenameField); @@ -55,9 +53,9 @@ LocalSaveActivity::LocalSaveActivity(SaveFile save, OnSaved onSaved_) : AddComponent(okayButton); SetOkayButton(okayButton); - if(save.GetGameSave()) + if(save->GetGameSave()) { - thumbnailRenderer = new ThumbnailRendererTask(save.GetGameSave(), Size.X-16, -1, false, true, false); + thumbnailRenderer = new ThumbnailRendererTask(*save->GetGameSave(), Size - Vec2(16, 16), true, false); thumbnailRenderer->Start(); } } @@ -83,9 +81,9 @@ void LocalSaveActivity::Save() } else if (filenameField->GetText().length()) { - ByteString finalFilename = ByteString(LOCAL_SAVE_DIR) + ByteString(PATH_SEP) + filenameField->GetText().ToUtf8() + ".cps"; - save.SetDisplayName(filenameField->GetText()); - save.SetFileName(finalFilename); + ByteString finalFilename = ByteString::Build(LOCAL_SAVE_DIR, PATH_SEP_CHAR, filenameField->GetText().ToUtf8(), ".cps"); + save->SetDisplayName(filenameField->GetText()); + save->SetFileName(finalFilename); if (Platform::FileExists(finalFilename)) { new ConfirmPrompt("Overwrite file", "Are you sure you wish to overwrite\n"+finalFilename.FromUtf8(), { [this, finalFilename] { @@ -106,15 +104,18 @@ void LocalSaveActivity::Save() void LocalSaveActivity::saveWrite(ByteString finalFilename) { Platform::MakeDirectory(LOCAL_SAVE_DIR); - GameSave *gameSave = save.GetGameSave(); Json::Value localSaveInfo; localSaveInfo["type"] = "localsave"; localSaveInfo["username"] = Client::Ref().GetAuthUser().Username; localSaveInfo["title"] = finalFilename; localSaveInfo["date"] = (Json::Value::UInt64)time(NULL); Client::Ref().SaveAuthorInfo(&localSaveInfo); - gameSave->authors = localSaveInfo; - auto [ fromNewerVersion, saveData ] = gameSave->Serialise(); + { + auto gameSave = save->TakeGameSave(); + gameSave->authors = localSaveInfo; + save->SetGameSave(std::move(gameSave)); + } + auto [ fromNewerVersion, saveData ] = save->GetGameSave()->Serialise(); (void)fromNewerVersion; if (saveData.size() == 0) new ErrorMessage("Error", "Unable to serialize game data."); @@ -124,7 +125,7 @@ void LocalSaveActivity::saveWrite(ByteString finalFilename) { if (onSaved) { - onSaved(&save); + onSaved(std::move(save)); } Exit(); } @@ -133,14 +134,15 @@ void LocalSaveActivity::saveWrite(ByteString finalFilename) void LocalSaveActivity::OnDraw() { Graphics * g = GetGraphics(); - g->draw_rgba_image(&save_to_disk_image[0], save_to_disk_imageW, save_to_disk_imageH, 0, 0, 0.7f); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->BlendRGBAImage(saveToDiskImage->data(), RectSized(Vec2(0, 0), saveToDiskImage->Size())); + g->DrawFilledRect(RectSized(Position, Size).Inset(-1), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); - if(thumbnail) + if (thumbnail) { - g->draw_image(thumbnail.get(), Position.X+(Size.X-thumbnail->Width)/2, Position.Y+45, 255); - g->drawrect(Position.X+(Size.X-thumbnail->Width)/2, Position.Y+45, thumbnail->Width, thumbnail->Height, 180, 180, 180, 255); + auto rect = RectSized(Position + Vec2((Size.X - thumbnail->Size().X) / 2, 45), thumbnail->Size()); + g->BlendImage(thumbnail->Data(), 0xFF, rect); + g->DrawRect(rect, 0xB4B4B4_rgb); } } diff --git a/src/gui/save/LocalSaveActivity.h b/src/gui/save/LocalSaveActivity.h index 5fa74eeb3..8c8b06f65 100644 --- a/src/gui/save/LocalSaveActivity.h +++ b/src/gui/save/LocalSaveActivity.h @@ -1,12 +1,15 @@ #pragma once -#include "Activity.h" -#include "client/SaveFile.h" -#include "graphics/Pixel.h" - #include #include #include +#include "Activity.h" +#include "client/SaveFile.h" +#include "common/Plane.h" +#include "Format.h" +#include "graphics/Pixel.h" + +#include "save_local.png.h" namespace ui { @@ -19,18 +22,19 @@ class ThumbnailRendererTask; class LocalSaveActivity: public WindowActivity { - using OnSaved = std::function; - std::vector save_to_disk_image; - int save_to_disk_imageW, save_to_disk_imageH; + using OnSaved = std::function)>; + std::unique_ptr>> saveToDiskImage = format::PixelsFromPNG( + std::vector(save_local_png, save_local_png + save_local_png_size) + ); - SaveFile save; + std::unique_ptr save; ThumbnailRendererTask *thumbnailRenderer; std::unique_ptr thumbnail; ui::Textbox * filenameField; OnSaved onSaved; - + public: - LocalSaveActivity(SaveFile save, OnSaved onSaved = nullptr); + LocalSaveActivity(std::unique_ptr newSave, OnSaved onSaved = nullptr); void saveWrite(ByteString finalFilename); void Save(); void OnDraw() override; diff --git a/src/gui/save/ServerSaveActivity.cpp b/src/gui/save/ServerSaveActivity.cpp index d5f3d7856..a955c14f9 100644 --- a/src/gui/save/ServerSaveActivity.cpp +++ b/src/gui/save/ServerSaveActivity.cpp @@ -1,7 +1,5 @@ #include "ServerSaveActivity.h" - #include "graphics/Graphics.h" - #include "gui/interface/Label.h" #include "gui/interface/Textbox.h" #include "gui/interface/Button.h" @@ -10,20 +8,16 @@ #include "gui/dialogues/SaveIDMessage.h" #include "gui/dialogues/ConfirmPrompt.h" #include "gui/dialogues/InformationMessage.h" - #include "client/Client.h" #include "client/ThumbnailRendererTask.h" #include "client/GameSave.h" - +#include "client/http/UploadSaveRequest.h" #include "tasks/Task.h" - #include "gui/Style.h" -#include "save_online.png.h" - class SaveUploadTask: public Task { - SaveInfo save; + SaveInfo &save; void before() override { @@ -38,37 +32,42 @@ class SaveUploadTask: public Task bool doWork() override { notifyProgress(-1); - return Client::Ref().UploadSave(save) == RequestOkay; + auto uploadSaveRequest = std::make_unique(save); + uploadSaveRequest->Start(); + uploadSaveRequest->Wait(); + try + { + save.SetID(uploadSaveRequest->Finish()); + } + catch (const http::RequestError &ex) + { + notifyError(ByteString(ex.what()).FromUtf8()); + return false; + } + return true; } public: - SaveInfo GetSave() - { - return save; - } - - SaveUploadTask(SaveInfo save): - save(save) + SaveUploadTask(SaveInfo &newSave): + save(newSave) { } }; -ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) : +ServerSaveActivity::ServerSaveActivity(std::unique_ptr newSave, OnUploaded onUploaded_) : WindowActivity(ui::Point(-1, -1), ui::Point(440, 200)), thumbnailRenderer(nullptr), - save(save), + save(std::move(newSave)), onUploaded(onUploaded_), saveUploadTask(NULL) { - PngDataToPixels(save_to_server_image, save_to_server_imageW, save_to_server_imageH, reinterpret_cast(save_online_png), save_online_png_size, false); - titleLabel = new ui::Label(ui::Point(4, 5), ui::Point((Size.X/2)-8, 16), ""); titleLabel->SetTextColour(style::Colour::InformationTitle); titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; titleLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; AddComponent(titleLabel); - CheckName(save.GetName()); //set titleLabel text + CheckName(save->GetName()); //set titleLabel text ui::Label * previewLabel = new ui::Label(ui::Point((Size.X/2)+4, 5), ui::Point((Size.X/2)-8, 16), "Preview:"); previewLabel->SetTextColour(style::Colour::InformationTitle); @@ -76,14 +75,14 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) : previewLabel->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; AddComponent(previewLabel); - nameField = new ui::Textbox(ui::Point(8, 25), ui::Point((Size.X/2)-16, 16), save.GetName(), "[save name]"); + nameField = new ui::Textbox(ui::Point(8, 25), ui::Point((Size.X/2)-16, 16), save->GetName(), "[save name]"); nameField->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; nameField->Appearance.HorizontalAlign = ui::Appearance::AlignLeft; nameField->SetActionCallback({ [this] { CheckName(nameField->GetText()); } }); AddComponent(nameField); FocusComponent(nameField); - descriptionField = new ui::Textbox(ui::Point(8, 65), ui::Point((Size.X/2)-16, Size.Y-(65+16+4)), save.GetDescription(), "[save description]"); + descriptionField = new ui::Textbox(ui::Point(8, 65), ui::Point((Size.X/2)-16, Size.Y-(65+16+4)), save->GetDescription(), "[save description]"); descriptionField->SetMultiline(true); descriptionField->SetLimit(254); descriptionField->Appearance.VerticalAlign = ui::Appearance::AlignTop; @@ -91,7 +90,7 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) : AddComponent(descriptionField); publishedCheckbox = new ui::Checkbox(ui::Point(8, 45), ui::Point((Size.X/2)-80, 16), "Publish", ""); - if(Client::Ref().GetAuthUser().Username != save.GetUserName()) + if(Client::Ref().GetAuthUser().Username != save->GetUserName()) { //Save is not owned by the user, disable by default publishedCheckbox->SetChecked(false); @@ -99,12 +98,12 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) : else { //Save belongs to the current user, use published state already set - publishedCheckbox->SetChecked(save.GetPublished()); + publishedCheckbox->SetChecked(save->GetPublished()); } AddComponent(publishedCheckbox); pausedCheckbox = new ui::Checkbox(ui::Point(160, 45), ui::Point(55, 16), "Paused", ""); - pausedCheckbox->SetChecked(save.GetGameSave()->paused); + pausedCheckbox->SetChecked(save->GetGameSave()->paused); AddComponent(pausedCheckbox); ui::Button * cancelButton = new ui::Button(ui::Point(0, Size.Y-16), ui::Point((Size.X/2)-75, 16), "Cancel"); @@ -145,22 +144,20 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, OnUploaded onUploaded_) : } }); AddComponent(RulesButton); - if (save.GetGameSave()) + if (save->GetGameSave()) { - thumbnailRenderer = new ThumbnailRendererTask(save.GetGameSave(), (Size.X/2)-16, -1, false, false, true); + thumbnailRenderer = new ThumbnailRendererTask(*save->GetGameSave(), Size / 2 - Vec2(16, 16), false, true); thumbnailRenderer->Start(); } } -ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, OnUploaded onUploaded_) : +ServerSaveActivity::ServerSaveActivity(std::unique_ptr newSave, bool saveNow, OnUploaded onUploaded_) : WindowActivity(ui::Point(-1, -1), ui::Point(200, 50)), thumbnailRenderer(nullptr), - save(save), + save(std::move(newSave)), onUploaded(onUploaded_), saveUploadTask(NULL) { - PngDataToPixels(save_to_server_image, save_to_server_imageW, save_to_server_imageH, reinterpret_cast(save_online_png), save_online_png_size, false); - ui::Label * titleLabel = new ui::Label(ui::Point(0, 0), Size, "Saving to server..."); titleLabel->SetTextColour(style::Colour::InformationTitle); titleLabel->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; @@ -169,7 +166,7 @@ ServerSaveActivity::ServerSaveActivity(SaveInfo save, bool saveNow, OnUploaded o AddAuthorInfo(); - saveUploadTask = new SaveUploadTask(this->save); + saveUploadTask = new SaveUploadTask(*this->save); saveUploadTask->AddTaskListener(this); saveUploadTask->Start(); } @@ -179,13 +176,13 @@ void ServerSaveActivity::NotifyDone(Task * task) if(!task->GetSuccess()) { Exit(); - new ErrorMessage("Error", Client::Ref().GetLastError()); + new ErrorMessage("Error", task->GetError()); } else { if (onUploaded) { - onUploaded(save); + onUploaded(std::move(save)); } Exit(); } @@ -193,24 +190,20 @@ void ServerSaveActivity::NotifyDone(Task * task) void ServerSaveActivity::Save() { - if(nameField->GetText().length()) + if (!nameField->GetText().length()) { - if(Client::Ref().GetAuthUser().Username != save.GetUserName() && publishedCheckbox->GetChecked()) - { - new ConfirmPrompt("Publish", "This save was created by " + save.GetUserName().FromUtf8() + ", you're about to publish this under your own name; If you haven't been given permission by the author to do so, please uncheck the publish box, otherwise continue", { [this] { - Exit(); - saveUpload(); - } }); - } - else - { - Exit(); + new ErrorMessage("Error", "You must specify a save name."); + return; + } + if(Client::Ref().GetAuthUser().Username != save->GetUserName() && publishedCheckbox->GetChecked()) + { + new ConfirmPrompt("Publish", "This save was created by " + save->GetUserName().FromUtf8() + ", you're about to publish this under your own name; If you haven't been given permission by the author to do so, please uncheck the publish box, otherwise continue", { [this] { saveUpload(); - } + } }); } else { - new ErrorMessage("Error", "You must specify a save name."); + saveUpload(); } } @@ -218,35 +211,36 @@ void ServerSaveActivity::AddAuthorInfo() { Json::Value serverSaveInfo; serverSaveInfo["type"] = "save"; - serverSaveInfo["id"] = save.GetID(); + serverSaveInfo["id"] = save->GetID(); serverSaveInfo["username"] = Client::Ref().GetAuthUser().Username; - serverSaveInfo["title"] = save.GetName().ToUtf8(); - serverSaveInfo["description"] = save.GetDescription().ToUtf8(); - serverSaveInfo["published"] = (int)save.GetPublished(); + serverSaveInfo["title"] = save->GetName().ToUtf8(); + serverSaveInfo["description"] = save->GetDescription().ToUtf8(); + serverSaveInfo["published"] = (int)save->GetPublished(); serverSaveInfo["date"] = (Json::Value::UInt64)time(NULL); Client::Ref().SaveAuthorInfo(&serverSaveInfo); - save.GetGameSave()->authors = serverSaveInfo; + { + auto gameSave = save->TakeGameSave(); + gameSave->authors = serverSaveInfo; + save->SetGameSave(std::move(gameSave)); + } } void ServerSaveActivity::saveUpload() { - save.SetName(nameField->GetText()); - save.SetDescription(descriptionField->GetText()); - save.SetPublished(publishedCheckbox->GetChecked()); - save.SetUserName(Client::Ref().GetAuthUser().Username); - save.SetID(0); - save.GetGameSave()->paused = pausedCheckbox->GetChecked(); + okayButton->Enabled = false; + save->SetName(nameField->GetText()); + save->SetDescription(descriptionField->GetText()); + save->SetPublished(publishedCheckbox->GetChecked()); + save->SetUserName(Client::Ref().GetAuthUser().Username); + save->SetID(0); + { + auto gameSave = save->TakeGameSave(); + gameSave->paused = pausedCheckbox->GetChecked(); + save->SetGameSave(std::move(gameSave)); + } AddAuthorInfo(); - - if(Client::Ref().UploadSave(save) != RequestOkay) - { - new ErrorMessage("Error", "Upload failed with error:\n"+Client::Ref().GetLastError()); - } - else if (onUploaded) - { - new SaveIDMessage(save.GetID()); - onUploaded(save); - } + uploadSaveRequest = std::make_unique(*save); + uploadSaveRequest->Start(); } void ServerSaveActivity::Exit() @@ -349,7 +343,7 @@ void ServerSaveActivity::ShowRules() void ServerSaveActivity::CheckName(String newname) { - if (newname.length() && newname == save.GetName() && save.GetUserName() == Client::Ref().GetAuthUser().Username) + if (newname.length() && newname == save->GetName() && save->GetUserName() == Client::Ref().GetAuthUser().Username) titleLabel->SetText("Modify simulation properties:"); else titleLabel->SetText("Upload new simulation:"); @@ -367,6 +361,26 @@ void ServerSaveActivity::OnTick(float dt) } } + if (uploadSaveRequest && uploadSaveRequest->CheckDone()) + { + okayButton->Enabled = true; + try + { + save->SetID(uploadSaveRequest->Finish()); + Exit(); + new SaveIDMessage(save->GetID()); + if (onUploaded) + { + onUploaded(std::move(save)); + } + } + catch (const http::RequestError &ex) + { + new ErrorMessage("Error", "Upload failed with error:\n" + ByteString(ex.what()).FromUtf8()); + } + uploadSaveRequest.reset(); + } + if(saveUploadTask) saveUploadTask->Poll(); } @@ -374,17 +388,18 @@ void ServerSaveActivity::OnTick(float dt) void ServerSaveActivity::OnDraw() { Graphics * g = GetGraphics(); - g->draw_rgba_image(&save_to_server_image[0], save_to_server_imageW, save_to_server_imageH, -10, 0, 0.7f); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->BlendRGBAImage(saveToServerImage->data(), RectSized(Vec2(-10, 0), saveToServerImage->Size())); + g->DrawFilledRect(RectSized(Position, Size).Inset(-1), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); - if(Size.X>220) - g->draw_line(Position.X+(Size.X/2)-1, Position.Y, Position.X+(Size.X/2)-1, Position.Y+Size.Y-1, 255, 255, 255, 255); + if (Size.X > 220) + g->DrawLine(Position + Vec2(Size.X / 2 - 1, 0), Position + Vec2(Size.X / 2 - 1, Size.Y - 1), 0xFFFFFF_rgb); - if(thumbnail) + if (thumbnail) { - g->draw_image(thumbnail.get(), Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Width)/2, Position.Y+25, 255); - g->drawrect(Position.X+(Size.X/2)+((Size.X/2)-thumbnail->Width)/2, Position.Y+25, thumbnail->Width, thumbnail->Height, 180, 180, 180, 255); + auto rect = RectSized(Position + Vec2(Size.X / 2 + (Size.X / 2 - thumbnail->Size().X) / 2, 25), thumbnail->Size()); + g->BlendImage(thumbnail->Data(), 0xFF, rect); + g->DrawRect(rect, 0xB4B4B4_rgb); } } diff --git a/src/gui/save/ServerSaveActivity.h b/src/gui/save/ServerSaveActivity.h index 48a91bc3a..8b64faaf2 100644 --- a/src/gui/save/ServerSaveActivity.h +++ b/src/gui/save/ServerSaveActivity.h @@ -1,13 +1,22 @@ #pragma once +#include +#include +#include + #include "Activity.h" #include "client/SaveInfo.h" -#include "tasks/TaskListener.h" +#include "common/Plane.h" +#include "Format.h" #include "graphics/Pixel.h" +#include "tasks/TaskListener.h" -#include -#include -#include +#include "save_online.png.h" + +namespace http +{ + class UploadSaveRequest; +} namespace ui { @@ -21,13 +30,16 @@ class Task; class VideoBuffer; class ServerSaveActivity: public WindowActivity, public TaskListener { - using OnUploaded = std::function; - std::vector save_to_server_image; - int save_to_server_imageW, save_to_server_imageH; + std::unique_ptr uploadSaveRequest; + + using OnUploaded = std::function)>; + std::unique_ptr>> saveToServerImage = format::PixelsFromPNG( + std::vector(save_online_png, save_online_png + save_online_png_size) + ); public: - ServerSaveActivity(SaveInfo save, OnUploaded onUploaded); - ServerSaveActivity(SaveInfo save, bool saveNow, OnUploaded onUploaded); + ServerSaveActivity(std::unique_ptr newSave, OnUploaded onUploaded); + ServerSaveActivity(std::unique_ptr newSave, bool saveNow, OnUploaded onUploaded); void saveUpload(); void Save(); virtual void Exit() override; @@ -42,7 +54,7 @@ protected: void NotifyDone(Task * task) override; ThumbnailRendererTask *thumbnailRenderer; std::unique_ptr thumbnail; - SaveInfo save; + std::unique_ptr save; private: OnUploaded onUploaded; protected: diff --git a/src/gui/search/SearchController.cpp b/src/gui/search/SearchController.cpp index f2f48d58d..7a1509edb 100644 --- a/src/gui/search/SearchController.cpp +++ b/src/gui/search/SearchController.cpp @@ -5,7 +5,15 @@ #include "SearchView.h" #include "client/Client.h" -#include "common/Platform.h" +#include "client/SaveInfo.h" +#include "client/GameSave.h" +#include "client/http/DeleteSaveRequest.h" +#include "client/http/PublishSaveRequest.h" +#include "client/http/UnpublishSaveRequest.h" +#include "client/http/FavouriteSaveRequest.h" +#include "client/http/SearchSavesRequest.h" +#include "client/http/SearchTagsRequest.h" +#include "common/platform/Platform.h" #include "common/tpt-minmax.h" #include "graphics/Graphics.h" #include "tasks/Task.h" @@ -14,6 +22,7 @@ #include "gui/dialogues/ConfirmPrompt.h" #include "gui/preview/PreviewController.h" #include "gui/preview/PreviewView.h" +#include "SimulationConfig.h" SearchController::SearchController(std::function onDone_): activePreview(NULL), @@ -33,14 +42,14 @@ SearchController::SearchController(std::function onDone_): onDone = onDone_; } -SaveInfo * SearchController::GetLoadedSave() +const SaveInfo *SearchController::GetLoadedSave() const { return searchModel->GetLoadedSave(); } -void SearchController::ReleaseLoadedSave() +std::unique_ptr SearchController::TakeLoadedSave() { - searchModel->SetLoadedSave(NULL); + return searchModel->TakeLoadedSave(); } void SearchController::Update() @@ -82,9 +91,11 @@ void SearchController::Exit() SearchController::~SearchController() { delete activePreview; - searchView->CloseActiveWindow(); delete searchModel; - delete searchView; + if (searchView->CloseActiveWindow()) + { + delete searchView; + } } void SearchController::DoSearch(String query, bool now) @@ -127,13 +138,13 @@ void SearchController::SetPageRelative(int offset) void SearchController::ChangeSort() { - if(searchModel->GetSort() == "new") + if(searchModel->GetSort() == http::sortByDate) { - searchModel->SetSort("best"); + searchModel->SetSort(http::sortByVotes); } else { - searchModel->SetSort("new"); + searchModel->SetSort(http::sortByDate); } searchModel->UpdateSaveList(1, searchModel->GetLastQuery()); } @@ -178,7 +189,7 @@ void SearchController::SelectAllSaves() if (!Client::Ref().GetAuthUser().UserID) return; if (searchModel->GetShowOwn() || - Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator || + Client::Ref().GetAuthUser().UserElevation == User::ElevationMod || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin) searchModel->SelectAllSaves(); @@ -193,29 +204,20 @@ void SearchController::OpenSaveDone() { if (activePreview->GetDoOpen() && activePreview->GetSaveInfo()) { - searchModel->SetLoadedSave(activePreview->GetSaveInfo()); + searchModel->SetLoadedSave(activePreview->TakeSaveInfo()); } else { - searchModel->SetLoadedSave(NULL); + searchModel->SetLoadedSave(nullptr); } } -void SearchController::OpenSave(int saveID) +void SearchController::OpenSave(int saveID, int saveDate, std::unique_ptr thumbnail) { delete activePreview; Graphics * g = searchView->GetGraphics(); - g->fillrect(XRES/3, WINDOWH-20, XRES/3, 20, 0, 0, 0, 150); //dim the "Page X of Y" a little to make the CopyTextButton more noticeable - activePreview = new PreviewController(saveID, 0, instantOpen, [this] { OpenSaveDone(); }); - activePreview->GetView()->MakeActiveWindow(); -} - -void SearchController::OpenSave(int saveID, int saveDate) -{ - delete activePreview; - Graphics * g = searchView->GetGraphics(); - g->fillrect(XRES/3, WINDOWH-20, XRES/3, 20, 0, 0, 0, 150); //dim the "Page X of Y" a little to make the CopyTextButton more noticeable - activePreview = new PreviewController(saveID, saveDate, instantOpen, [this] { OpenSaveDone(); }); + g->BlendFilledRect(RectSized(Vec2{ XRES/3, WINDOWH-20 }, Vec2{ XRES/3, 20 }), 0x000000_rgb .WithAlpha(150)); //dim the "Page X of Y" a little to make the CopyTextButton more noticeable + activePreview = new PreviewController(saveID, saveDate, instantOpen, [this] { OpenSaveDone(); }, std::move(thumbnail)); activePreview->GetView()->MakeActiveWindow(); } @@ -249,9 +251,16 @@ void SearchController::removeSelectedC() for (size_t i = 0; i < saves.size(); i++) { notifyStatus(String::Build("Deleting save [", saves[i], "] ...")); - if (Client::Ref().DeleteSave(saves[i])!=RequestOkay) + auto deleteSaveRequest = std::make_unique(saves[i]); + deleteSaveRequest->Start(); + deleteSaveRequest->Wait(); + try { - notifyError(String::Build("Failed to delete [", saves[i], "]: ", Client::Ref().GetLastError())); + deleteSaveRequest->Finish(); + } + catch (const http::RequestError &ex) + { + notifyError(String::Build("Failed to delete [", saves[i], "]: ", ByteString(ex.what()).FromAscii())); c->Refresh(); return false; } @@ -290,37 +299,49 @@ void SearchController::unpublishSelectedC(bool publish) public: UnpublishSavesTask(std::vector saves_, SearchController *c_, bool publish_) { saves = saves_; c = c_; publish = publish_; } - bool PublishSave(int saveID) + void PublishSave(int saveID) { notifyStatus(String::Build("Publishing save [", saveID, "]")); - if (Client::Ref().PublishSave(saveID) != RequestOkay) - return false; - return true; + auto publishSaveRequest = std::make_unique(saveID); + publishSaveRequest->Start(); + publishSaveRequest->Wait(); + publishSaveRequest->Finish(); } - bool UnpublishSave(int saveID) + void UnpublishSave(int saveID) { notifyStatus(String::Build("Unpublishing save [", saveID, "]")); - if (Client::Ref().UnpublishSave(saveID) != RequestOkay) - return false; - return true; + auto unpublishSaveRequest = std::make_unique(saveID); + unpublishSaveRequest->Start(); + unpublishSaveRequest->Wait(); + unpublishSaveRequest->Finish(); } bool doWork() override { - bool ret; for (size_t i = 0; i < saves.size(); i++) { - if (publish) - ret = PublishSave(saves[i]); - else - ret = UnpublishSave(saves[i]); - if (!ret) + try + { + if (publish) + { + PublishSave(saves[i]); + } + else + { + UnpublishSave(saves[i]); + } + } + catch (const http::RequestError &ex) { if (publish) // uses html page so error message will be spam + { notifyError(String::Build("Failed to publish [", saves[i], "], is this save yours?")); + } else - notifyError(String::Build("Failed to unpublish [", saves[i], "]: " + Client::Ref().GetLastError())); + { + notifyError(String::Build("Failed to unpublish [", saves[i], "]: ", ByteString(ex.what()).FromAscii())); + } c->Refresh(); return false; } @@ -347,9 +368,16 @@ void SearchController::FavouriteSelected() for (size_t i = 0; i < saves.size(); i++) { notifyStatus(String::Build("Favouring save [", saves[i], "]")); - if (Client::Ref().FavouriteSave(saves[i], true)!=RequestOkay) + auto favouriteSaveRequest = std::make_unique(saves[i], true); + favouriteSaveRequest->Start(); + favouriteSaveRequest->Wait(); + try { - notifyError(String::Build("Failed to favourite [", saves[i], "]: " + Client::Ref().GetLastError())); + favouriteSaveRequest->Finish(); + } + catch (const http::RequestError &ex) + { + notifyError(String::Build("Failed to favourite [", saves[i], "]: ", ByteString(ex.what()).FromAscii())); return false; } notifyProgress((i + 1) * 100 / saves.size()); @@ -368,9 +396,16 @@ void SearchController::FavouriteSelected() for (size_t i = 0; i < saves.size(); i++) { notifyStatus(String::Build("Unfavouring save [", saves[i], "]")); - if (Client::Ref().FavouriteSave(saves[i], false)!=RequestOkay) + auto unfavouriteSaveRequest = std::make_unique(saves[i], false); + unfavouriteSaveRequest->Start(); + unfavouriteSaveRequest->Wait(); + try { - notifyError(String::Build("Failed to unfavourite [", saves[i], "]: " + Client::Ref().GetLastError())); + unfavouriteSaveRequest->Finish(); + } + catch (const http::RequestError &ex) + { + notifyError(String::Build("Failed to unfavourite [", saves[i], "]: ", ByteString(ex.what()).FromAscii())); return false; } notifyProgress((i + 1) * 100 / saves.size()); diff --git a/src/gui/search/SearchController.h b/src/gui/search/SearchController.h index cc11883c3..cc33fa5e8 100644 --- a/src/gui/search/SearchController.h +++ b/src/gui/search/SearchController.h @@ -1,16 +1,14 @@ -#ifndef SEARCHCONTROLLER_H -#define SEARCHCONTROLLER_H -#include "Config.h" - +#pragma once #include "common/String.h" - #include +#include class SaveInfo; class PreviewController; class PreviewController; class SearchView; class SearchModel; +class VideoBuffer; class SearchController { private: @@ -45,15 +43,12 @@ public: void Selected(int saveID, bool selected); void SelectAllSaves(); void InstantOpen(bool instant); - void OpenSave(int saveID); - void OpenSave(int saveID, int saveDate); + void OpenSave(int saveID, int saveDate, std::unique_ptr thumbnail); void Update(); void ClearSelection(); void RemoveSelected(); void UnpublishSelected(bool publish); void FavouriteSelected(); - void ReleaseLoadedSave(); - SaveInfo * GetLoadedSave(); + const SaveInfo *GetLoadedSave() const; + std::unique_ptr TakeLoadedSave(); }; - -#endif // SEARCHCONTROLLER_H diff --git a/src/gui/search/SearchModel.cpp b/src/gui/search/SearchModel.cpp index 3208de8e6..1b03ea377 100644 --- a/src/gui/search/SearchModel.cpp +++ b/src/gui/search/SearchModel.cpp @@ -1,30 +1,22 @@ #include "SearchModel.h" - #include "SearchView.h" - +#include "Format.h" #include "client/SaveInfo.h" +#include "client/GameSave.h" #include "client/Client.h" - +#include "client/http/SearchSavesRequest.h" +#include "client/http/SearchTagsRequest.h" +#include "common/tpt-minmax.h" #include #include -#include "common/tpt-minmax.h" - SearchModel::SearchModel(): - loadedSave(NULL), - currentSort("best"), + currentSort(http::sortByVotes), currentPage(1), resultCount(0), showOwn(false), showFavourite(false), - showTags(true), - saveListLoaded(false), - updateSaveListWorking(false), - updateSaveListFinished(false), - updateSaveListResult(nullptr), - updateTagListWorking(false), - updateTagListFinished(false), - updateTagListResult(nullptr) + showTags(true) { } @@ -38,32 +30,63 @@ bool SearchModel::GetShowTags() return showTags; } -void SearchModel::updateSaveListT() +void SearchModel::BeginSearchSaves(int start, int count, String query, http::Sort sort, http::Category category) { - ByteString category = ""; - if(showFavourite) - category = "Favourites"; - if(showOwn && Client::Ref().GetAuthUser().UserID) - category = "by:"+Client::Ref().GetAuthUser().Username; - std::vector * saveList = Client::Ref().SearchSaves((currentPage-1)*20, 20, lastQuery, currentSort=="new"?"date":"votes", category, thResultCount); - - updateSaveListResult = saveList; - updateSaveListFinished = true; + lastError = ""; + resultCount = 0; + searchSaves = std::make_unique(start, count, query.ToUtf8(), sort, category); + searchSaves->Start(); } -void SearchModel::updateTagListT() +std::vector> SearchModel::EndSearchSaves() { - int tagResultCount; - std::vector > * tagList = Client::Ref().GetTags(0, 24, "", tagResultCount); + std::vector> saveArray; + try + { + std::tie(resultCount, saveArray) = searchSaves->Finish(); + } + catch (const http::RequestError &ex) + { + lastError = ByteString(ex.what()).FromUtf8(); + } + searchSaves.reset(); + return saveArray; +} - updateTagListResult = tagList; - updateTagListFinished = true; +void SearchModel::BeginGetTags(int start, int count, String query) +{ + lastError = ""; + ByteStringBuilder urlStream; + urlStream << SCHEME << SERVER << "/Browse/Tags.json?Start=" << start << "&Count=" << count; + if(query.length()) + { + urlStream << "&Search_Query="; + if(query.length()) + urlStream << format::URLEncode(query.ToUtf8()); + } + getTags = std::make_unique(start, count, query.ToUtf8()); + getTags->Start(); +} + +std::vector> SearchModel::EndGetTags() +{ + std::vector> tagArray; + try + { + tagArray = getTags->Finish(); + } + catch (const http::RequestError &ex) + { + lastError = ByteString(ex.what()).FromUtf8(); + } + getTags.reset(); + return tagArray; } bool SearchModel::UpdateSaveList(int pageNumber, String query) { //Threading - if (!updateSaveListWorking) + if (!searchSaves) { lastQuery = query; lastError = ""; @@ -72,7 +95,7 @@ bool SearchModel::UpdateSaveList(int pageNumber, String query) //resultCount = 0; currentPage = pageNumber; - if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == "best" && query == "") + if(pageNumber == 1 && !showOwn && !showFavourite && currentSort == http::sortByVotes && query == "") SetShowTags(true); else SetShowTags(false); @@ -83,42 +106,48 @@ bool SearchModel::UpdateSaveList(int pageNumber, String query) selected.clear(); notifySelectedChanged(); - if(GetShowTags() && !tagList.size() && !updateTagListWorking) + if (GetShowTags() && !tagList.size() && !getTags) { - updateTagListFinished = false; - updateTagListWorking = true; - std::thread([this]() { updateTagListT(); }).detach(); + BeginGetTags(0, 24, ""); } - updateSaveListFinished = false; - updateSaveListWorking = true; - std::thread([this]() { updateSaveListT(); }).detach(); + auto category = http::categoryNone; + if (showFavourite) + { + category = http::categoryFavourites; + } + if (showOwn && Client::Ref().GetAuthUser().UserID) + { + category = http::categoryMyOwn; + } + BeginSearchSaves((currentPage-1)*20, 20, lastQuery, currentSort, category); return true; } return false; } -void SearchModel::SetLoadedSave(SaveInfo * save) +void SearchModel::SetLoadedSave(std::unique_ptr save) { - if(loadedSave != save && loadedSave) - delete loadedSave; - if(save) - { - loadedSave = new SaveInfo(*save); - } - else - { - loadedSave = NULL; - } + loadedSave = std::move(save); } -SaveInfo * SearchModel::GetLoadedSave(){ - return loadedSave; +const SaveInfo *SearchModel::GetLoadedSave() const +{ + return loadedSave.get(); } -std::vector SearchModel::GetSaveList() +std::unique_ptr SearchModel::TakeLoadedSave() { - return saveList; + return std::move(loadedSave); +} + +std::vector SearchModel::GetSaveList() // non-owning +{ + std::vector nonOwningSaveList; + std::transform(saveList.begin(), saveList.end(), std::back_inserter(nonOwningSaveList), [](auto &ptr) { + return ptr.get(); + }); + return nonOwningSaveList; } std::vector > SearchModel::GetTagList() @@ -128,51 +157,19 @@ std::vector > SearchModel::GetTagList() void SearchModel::Update() { - if(updateSaveListWorking) + if (searchSaves && searchSaves->CheckDone()) { - if(updateSaveListFinished) - { - updateSaveListWorking = false; - lastError = ""; - saveListLoaded = true; - - std::vector *tempSaveList = updateSaveListResult; - updateSaveListResult = nullptr; - - if(tempSaveList) - { - saveList = *tempSaveList; - delete tempSaveList; - } - - if(!saveList.size()) - { - lastError = Client::Ref().GetLastError(); - if (lastError == "Unspecified Error") - lastError = ""; - } - - resultCount = thResultCount; - notifyPageChanged(); - notifySaveListChanged(); - } + saveListLoaded = true; + lastError = ""; + saveList = EndSearchSaves(); + notifyPageChanged(); + notifySaveListChanged(); } - if(updateTagListWorking) + if (getTags && getTags->CheckDone()) { - if(updateTagListFinished) - { - updateTagListWorking = false; - - std::vector> *tempTagList = updateTagListResult; - updateTagListResult = nullptr; - - if(tempTagList) - { - tagList = *tempTagList; - delete tempTagList; - } - notifyTagListChanged(); - } + lastError = ""; + tagList = EndGetTags(); + notifyTagListChanged(); } } @@ -297,14 +294,9 @@ void SearchModel::notifySelectedChanged() } } -SearchModel::~SearchModel() -{ - delete loadedSave; -} - int SearchModel::GetPageCount() { - if (!showOwn && !showFavourite && currentSort == "best" && lastQuery == "") + if (!showOwn && !showFavourite && 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))); diff --git a/src/gui/search/SearchModel.h b/src/gui/search/SearchModel.h index d0249a661..51a7fa177 100644 --- a/src/gui/search/SearchModel.h +++ b/src/gui/search/SearchModel.h @@ -1,27 +1,40 @@ -#ifndef SEARCHMODEL_H -#define SEARCHMODEL_H -#include "Config.h" - -#include +#pragma once #include "common/String.h" +#include "client/Search.h" +#include "Config.h" +#include #include +#include + +namespace http +{ + class SearchSavesRequest; + class SearchTagsRequest; +} class SaveInfo; class SearchView; class SearchModel { private: - SaveInfo * loadedSave; - ByteString currentSort; + std::unique_ptr searchSaves; + void BeginSearchSaves(int start, int count, String query, http::Sort sort, http::Category category); + std::vector> EndSearchSaves(); + + void BeginGetTags(int start, int count, String query); + std::vector> EndGetTags(); + std::unique_ptr getTags; + + std::unique_ptr loadedSave; + http::Sort currentSort; String lastQuery; String lastError; std::vector selected; std::vector observers; - std::vector saveList; + std::vector> saveList; std::vector > tagList; int currentPage; int resultCount; - int thResultCount; bool showOwn; bool showFavourite; bool showTags; @@ -34,38 +47,29 @@ private: void notifyShowFavouriteChanged(); //Variables and methods for background save request - bool saveListLoaded; - bool updateSaveListWorking; - std::atomic updateSaveListFinished; - void updateSaveListT(); - std::vector *updateSaveListResult; - - bool updateTagListWorking; - std::atomic updateTagListFinished; - void updateTagListT(); - std::vector> *updateTagListResult; + bool saveListLoaded = false; public: SearchModel(); - virtual ~SearchModel(); void SetShowTags(bool show); bool GetShowTags(); void AddObserver(SearchView * observer); bool UpdateSaveList(int pageNumber, String query); - std::vector GetSaveList(); + std::vector GetSaveList(); // non-owning std::vector > GetTagList(); String GetLastError() { return lastError; } int GetPageCount(); int GetPageNum() { return currentPage; } String GetLastQuery() { return lastQuery; } - void SetSort(ByteString sort) { if(!updateSaveListWorking) { currentSort = sort; } notifySortChanged(); } - ByteString GetSort() { return currentSort; } - void SetShowOwn(bool show) { if(!updateSaveListWorking) { if(show!=showOwn) { showOwn = show; } } notifyShowOwnChanged(); } + 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(); } bool GetShowOwn() { return showOwn; } - void SetShowFavourite(bool show) { if(show!=showFavourite && !updateSaveListWorking) { showFavourite = show; } notifyShowFavouriteChanged(); } + void SetShowFavourite(bool show) { if(show!=showFavourite && !searchSaves) { showFavourite = show; } notifyShowFavouriteChanged(); } bool GetShowFavourite() { return showFavourite; } - void SetLoadedSave(SaveInfo * save); - SaveInfo * GetLoadedSave(); + void SetLoadedSave(std::unique_ptr save); + const SaveInfo *GetLoadedSave() const; + std::unique_ptr TakeLoadedSave(); bool GetSavesLoaded() { return saveListLoaded; } std::vector GetSelected() { return selected; } void ClearSelected() { selected.clear(); notifySelectedChanged(); } @@ -74,5 +78,3 @@ public: void DeselectSave(int saveID); void Update(); }; - -#endif // SEARCHMODEL_H diff --git a/src/gui/search/SearchView.cpp b/src/gui/search/SearchView.cpp index 85ec4c341..6f1ede05a 100644 --- a/src/gui/search/SearchView.cpp +++ b/src/gui/search/SearchView.cpp @@ -1,23 +1,18 @@ #include "SearchView.h" - #include "SearchController.h" #include "SearchModel.h" - #include "client/Client.h" #include "client/SaveInfo.h" - -#include "gui/interface/Keys.h" #include "gui/interface/SaveButton.h" #include "gui/interface/Button.h" #include "gui/interface/Label.h" #include "gui/interface/RichLabel.h" #include "gui/interface/Textbox.h" #include "gui/interface/Spinner.h" - -#include "PowderToy.h" -#include "Config.h" - +#include "PowderToySDL.h" #include "graphics/Graphics.h" +#include "SimulationConfig.h" +#include #ifdef GetUserName # undef GetUserName // dammit windows @@ -216,7 +211,7 @@ void SearchView::Search(String query) void SearchView::NotifySortChanged(SearchModel * sender) { - if(sender->GetSort() == "best") + if(sender->GetSort() == http::sortByVotes) { sortButton->SetToggleState(false); sortButton->SetText("By votes"); @@ -233,7 +228,7 @@ void SearchView::NotifySortChanged(SearchModel * sender) void SearchView::NotifyShowOwnChanged(SearchModel * sender) { ownButton->SetToggleState(sender->GetShowOwn()); - if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod) { unpublishSelected->Enabled = true; removeSelected->Enabled = true; @@ -253,7 +248,7 @@ void SearchView::NotifyShowFavouriteChanged(SearchModel * sender) unpublishSelected->Enabled = false; removeSelected->Enabled = false; } - else if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + else if(sender->GetShowOwn() || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod) { unpublishSelected->Enabled = true; removeSelected->Enabled = true; @@ -276,7 +271,7 @@ void SearchView::NotifyPageChanged(SearchModel * sender) { String pageInfo = String::Build("of ", pageCount); pageCountLabel->SetText(pageInfo); - int width = Graphics::textwidth(pageInfo); + int width = Graphics::TextSize(pageInfo).X - 1; pageLabel->Position.X = WINDOWW/2-width-20; pageTextbox->Position.X = WINDOWW/2-width+11; @@ -328,7 +323,7 @@ void SearchView::CheckAccess() favButton->Enabled = true; favouriteSelected->Enabled = true; - if (Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + if (Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod) { unpublishSelected->Enabled = true; removeSelected->Enabled = true; @@ -461,7 +456,7 @@ void SearchView::NotifySaveListChanged(SearchModel * sender) int buttonWidth, buttonHeight, saveX = 0, saveY = 0, savesX = 5, savesY = 4, buttonPadding = 1; int buttonAreaWidth, buttonAreaHeight, buttonXOffset, buttonYOffset; - std::vector saves = sender->GetSaveList(); + auto saves = sender->GetSaveList(); //string messageOfTheDay = sender->GetMessageOfTheDay(); if(sender->GetShowFavourite()) @@ -567,14 +562,14 @@ void SearchView::NotifySaveListChanged(SearchModel * sender) saves[i]); saveButton->AddContextMenu(0); saveButton->SetActionCallback({ - [this, saveButton] { c->OpenSave(saveButton->GetSave()->GetID(), saveButton->GetSave()->GetVersion()); }, + [this, saveButton] { c->OpenSave(saveButton->GetSave()->GetID(), saveButton->GetSave()->GetVersion(), saveButton->CloneThumbnail()); }, [this, saveButton] { Search(String::Build("history:", saveButton->GetSave()->GetID())); }, [this, saveButton] { Search(String::Build("user:", saveButton->GetSave()->GetUserName().FromUtf8())); }, [this, saveButton] { c->Selected(saveButton->GetSave()->GetID(), saveButton->GetSelected()); } }); if(Client::Ref().GetAuthUser().UserID) saveButton->SetSelectable(true); - if (saves[i]->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + if (saves[i]->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod) saveButton->SetShowVotes(true); saveButtons.push_back(saveButton); AddComponent(saveButton); diff --git a/src/gui/search/SearchView.h b/src/gui/search/SearchView.h index 38314906d..4e417ee06 100644 --- a/src/gui/search/SearchView.h +++ b/src/gui/search/SearchView.h @@ -1,9 +1,7 @@ -#ifndef SEARCHVIEW_H -#define SEARCHVIEW_H - -#include +#pragma once #include "client/ClientListener.h" #include "gui/interface/Window.h" +#include namespace ui { @@ -71,5 +69,3 @@ public: void OnKeyRelease(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override; }; - -#endif // SEARCHVIEW_H diff --git a/src/gui/tags/TagsController.cpp b/src/gui/tags/TagsController.cpp index 4a28f5e3f..df9ec12b3 100644 --- a/src/gui/tags/TagsController.cpp +++ b/src/gui/tags/TagsController.cpp @@ -1,8 +1,8 @@ #include "TagsController.h" - #include "TagsModel.h" #include "TagsView.h" - +#include "client/http/AddTagRequest.h" +#include "client/http/RemoveTagRequest.h" #include "gui/interface/Engine.h" #include "client/SaveInfo.h" #include "Controller.h" @@ -36,6 +36,11 @@ void TagsController::AddTag(ByteString tag) tagsModel->AddTag(tag); } +void TagsController::Tick() +{ + tagsModel->Tick(); +} + void TagsController::Exit() { tagsView->CloseActiveWindow(); @@ -46,8 +51,10 @@ void TagsController::Exit() TagsController::~TagsController() { - tagsView->CloseActiveWindow(); delete tagsModel; - delete tagsView; + if (tagsView->CloseActiveWindow()) + { + delete tagsView; + } } diff --git a/src/gui/tags/TagsController.h b/src/gui/tags/TagsController.h index 44c0627d5..d0c69f625 100644 --- a/src/gui/tags/TagsController.h +++ b/src/gui/tags/TagsController.h @@ -1,9 +1,5 @@ -#ifndef TAGSCONTROLLER_H_ -#define TAGSCONTROLLER_H_ -#include "Config.h" - +#pragma once #include "common/String.h" - #include class SaveInfo; @@ -22,7 +18,6 @@ public: void RemoveTag(ByteString tag); void AddTag(ByteString tag); void Exit(); + void Tick(); virtual ~TagsController(); }; - -#endif /* TAGSCONTROLLER_H_ */ diff --git a/src/gui/tags/TagsModel.cpp b/src/gui/tags/TagsModel.cpp index 76bc586c6..4ed33a5ab 100644 --- a/src/gui/tags/TagsModel.cpp +++ b/src/gui/tags/TagsModel.cpp @@ -1,62 +1,91 @@ #include "TagsModel.h" - #include "TagsView.h" #include "TagsModelException.h" - #include "client/Client.h" #include "client/SaveInfo.h" +#include "client/http/AddTagRequest.h" +#include "client/http/RemoveTagRequest.h" +#include "gui/dialogues/ErrorMessage.h" -TagsModel::TagsModel(): - save(NULL) +void TagsModel::SetSave(SaveInfo *newSave /* non-owning */) { - -} - -void TagsModel::SetSave(SaveInfo * save) -{ - this->save = save; + queuedTags.clear(); + this->save = newSave; notifyTagsChanged(); } -SaveInfo * TagsModel::GetSave() +SaveInfo *TagsModel::GetSave() // non-owning { return save; } -void TagsModel::RemoveTag(ByteString tag) +void TagsModel::Tick() { - if(save) + auto triggerTags = false; + std::list tags; + if (addTagRequest && addTagRequest->CheckDone()) { - std::list * tags = Client::Ref().RemoveTag(save->GetID(), tag); - if(tags) + try { - save->SetTags(std::list(*tags)); - notifyTagsChanged(); - delete tags; + tags = addTagRequest->Finish(); + triggerTags = true; } - else + catch (const http::RequestError &ex) { - throw TagsModelException(Client::Ref().GetLastError()); + new ErrorMessage("Could not add tag", ByteString(ex.what()).FromUtf8()); + } + addTagRequest.reset(); + } + if (removeTagRequest && removeTagRequest->CheckDone()) + { + try + { + tags = removeTagRequest->Finish(); + triggerTags = true; + } + catch (const http::RequestError &ex) + { + new ErrorMessage("Could not remove tag", ByteString(ex.what()).FromUtf8()); + } + removeTagRequest.reset(); + } + if (triggerTags) + { + if (save) + { + save->SetTags(tags); + } + notifyTagsChanged(); + } + if (!addTagRequest && !removeTagRequest && !queuedTags.empty()) + { + auto it = queuedTags.begin(); + auto [ tag, add ] = *it; + queuedTags.erase(it); + if (save) + { + if (add) + { + addTagRequest = std::make_unique(save->GetID(), tag); + addTagRequest->Start(); + } + else + { + removeTagRequest = std::make_unique(save->GetID(), tag); + removeTagRequest->Start(); + } } } } +void TagsModel::RemoveTag(ByteString tag) +{ + queuedTags[tag] = false; +} + void TagsModel::AddTag(ByteString tag) { - if(save) - { - std::list * tags = Client::Ref().AddTag(save->GetID(), tag); - if(tags) - { - save->SetTags(std::list(*tags)); - notifyTagsChanged(); - delete tags; - } - else - { - throw TagsModelException(Client::Ref().GetLastError()); - } - } + queuedTags[tag] = true; } void TagsModel::AddObserver(TagsView * observer) @@ -72,7 +101,3 @@ void TagsModel::notifyTagsChanged() observers[i]->NotifyTagsChanged(this); } } - -TagsModel::~TagsModel() { -} - diff --git a/src/gui/tags/TagsModel.h b/src/gui/tags/TagsModel.h index fdb109f13..b1a9d1bce 100644 --- a/src/gui/tags/TagsModel.h +++ b/src/gui/tags/TagsModel.h @@ -1,25 +1,30 @@ -#ifndef TAGSMODEL_H_ -#define TAGSMODEL_H_ -#include "Config.h" - -#include +#pragma once #include "common/String.h" +#include +#include +#include + +namespace http +{ + class AddTagRequest; + class RemoveTagRequest; +} class SaveInfo; class TagsView; class TagsModel { - SaveInfo * save; + std::unique_ptr addTagRequest; + std::unique_ptr removeTagRequest; + std::map queuedTags; + SaveInfo *save = nullptr; // non-owning std::vector observers; void notifyTagsChanged(); public: - TagsModel(); void AddObserver(TagsView * observer); - void SetSave(SaveInfo * save); + void SetSave(SaveInfo *newSave /* non-owning */); void RemoveTag(ByteString tag); void AddTag(ByteString tag); - SaveInfo * GetSave(); - virtual ~TagsModel(); + SaveInfo *GetSave(); // non-owning + void Tick(); }; - -#endif /* TAGSMODEL_H_ */ diff --git a/src/gui/tags/TagsModelException.h b/src/gui/tags/TagsModelException.h index 467153f31..dd83db094 100644 --- a/src/gui/tags/TagsModelException.h +++ b/src/gui/tags/TagsModelException.h @@ -1,6 +1,4 @@ -#ifndef TAGSMODELEXCEPTION_H_ -#define TAGSMODELEXCEPTION_H_ - +#pragma once #include "common/String.h" #include @@ -11,5 +9,3 @@ public: const char * what() const throw() override { return message.c_str(); }; ~TagsModelException() throw() {}; }; - -#endif /* TAGSMODELEXCEPTION_H_ */ diff --git a/src/gui/tags/TagsView.cpp b/src/gui/tags/TagsView.cpp index ca57cbf31..b366cc8c7 100644 --- a/src/gui/tags/TagsView.cpp +++ b/src/gui/tags/TagsView.cpp @@ -13,7 +13,8 @@ #include "gui/interface/Button.h" #include "gui/interface/Textbox.h" #include "gui/interface/Label.h" -#include "gui/interface/Keys.h" + +#include TagsView::TagsView(): ui::Window(ui::Point(-1, -1), ui::Point(195, 250)) @@ -48,11 +49,16 @@ TagsView::TagsView(): AddComponent(title); } +void TagsView::OnTick(float dt) +{ + c->Tick(); +} + void TagsView::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); } void TagsView::NotifyTagsChanged(TagsModel * sender) @@ -75,7 +81,7 @@ void TagsView::NotifyTagsChanged(TagsModel * sender) tags.push_back(tempLabel); AddComponent(tempLabel); - if(sender->GetSave()->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationModerator) + if(sender->GetSave()->GetUserName() == Client::Ref().GetAuthUser().Username || Client::Ref().GetAuthUser().UserElevation == User::ElevationAdmin || Client::Ref().GetAuthUser().UserElevation == User::ElevationMod) { ui::Button * tempButton = new ui::Button(ui::Point(15, 37+(16*i)), ui::Point(11, 12)); tempButton->Appearance.icon = IconDelete; @@ -84,14 +90,7 @@ void TagsView::NotifyTagsChanged(TagsModel * sender) tempButton->Appearance.HorizontalAlign = ui::Appearance::AlignCentre; tempButton->Appearance.VerticalAlign = ui::Appearance::AlignMiddle; tempButton->SetActionCallback({ [this, tag] { - try - { - c->RemoveTag(tag); - } - catch(TagsModelException & ex) - { - new ErrorMessage("Could not remove tag", ByteString(ex.what()).FromUtf8()); - } + c->RemoveTag(tag); } }); tags.push_back(tempButton); AddComponent(tempButton); @@ -124,13 +123,6 @@ void TagsView::addTag() new ErrorMessage("Tag not long enough", "Must be at least 4 letters"); return; } - try - { - c->AddTag(tagInput->GetText().ToUtf8()); - } - catch(TagsModelException & ex) - { - new ErrorMessage("Could not add tag", ByteString(ex.what()).FromUtf8()); - } + c->AddTag(tagInput->GetText().ToUtf8()); tagInput->SetText(""); } diff --git a/src/gui/tags/TagsView.h b/src/gui/tags/TagsView.h index 318491160..6e3a92295 100644 --- a/src/gui/tags/TagsView.h +++ b/src/gui/tags/TagsView.h @@ -1,8 +1,6 @@ -#ifndef TAGSVIEW_H_ -#define TAGSVIEW_H_ - -#include +#pragma once #include "gui/interface/Window.h" +#include namespace ui { @@ -24,9 +22,8 @@ class TagsView: public ui::Window { public: TagsView(); void OnDraw() override; + void OnTick(float dt) override; void AttachController(TagsController * c_) { c = c_; } void OnKeyPress(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt) override; void NotifyTagsChanged(TagsModel * sender); }; - -#endif /* TAGSVIEW_H_ */ diff --git a/src/gui/update/UpdateActivity.cpp b/src/gui/update/UpdateActivity.cpp index da25e89c1..bad425682 100644 --- a/src/gui/update/UpdateActivity.cpp +++ b/src/gui/update/UpdateActivity.cpp @@ -1,19 +1,14 @@ -#include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows - #include "UpdateActivity.h" - -#include - -#include "Config.h" -#include "Update.h" - -#include "client/Client.h" -#include "common/Platform.h" +#include "client/http/Request.h" +#include "prefs/GlobalPrefs.h" +#include "common/platform/Platform.h" #include "tasks/Task.h" #include "tasks/TaskWindow.h" - #include "gui/dialogues/ConfirmPrompt.h" #include "gui/interface/Engine.h" +#include "Config.h" +#include +#include class UpdateDownloadTask : public Task { @@ -31,32 +26,49 @@ private: } bool doWork() override { - String error; - http::Request *request = new http::Request(updateName); + auto &prefs = GlobalPrefs::Ref(); + + auto niceNotifyError = [this](String error) { + notifyError("Downloaded update is corrupted\n" + error); + return false; + }; + + auto request = std::make_unique(updateName); request->Start(); notifyStatus("Downloading update"); notifyProgress(-1); while(!request->CheckDone()) { int total, done; - request->CheckProgress(&total, &done); - notifyProgress(total ? done * 100 / total : 0); + std::tie(total, done) = request->CheckProgress(); + if (total == -1) + { + notifyProgress(-1); + } + else + { + notifyProgress(total ? done * 100 / total : 0); + } Platform::Millisleep(1); } int status; - ByteString data = request->Finish(&status); + ByteString data; + try + { + std::tie(status, data) = request->Finish(); + } + catch (const http::RequestError &ex) + { + return niceNotifyError("Could not download update: " + String::Build("Server responded with Status ", ByteString(ex.what()).FromAscii())); + } if (status!=200) { - error = String::Build("Server responded with Status ", status); - notifyError("Could not download update: " + error); - return false; + return niceNotifyError("Could not download update: " + String::Build("Server responded with Status ", status)); } if (!data.size()) { - error = "Server responded with nothing"; - notifyError("Server did not return any data"); - return false; + return niceNotifyError("Server did not return any data"); } notifyStatus("Unpacking update"); @@ -66,13 +78,11 @@ private: if(data.size()<16) { - error = String::Build("Unsufficient data, got ", data.size(), " bytes"); - goto corrupt; + return niceNotifyError(String::Build("Unsufficient data, got ", data.size(), " bytes")); } if (data[0]!=0x42 || data[1]!=0x75 || data[2]!=0x54 || data[3]!=0x54) { - error = "Invalid update format"; - goto corrupt; + return niceNotifyError("Invalid update format"); } uncompressedLength = (unsigned char)data[4]; @@ -80,51 +90,34 @@ private: uncompressedLength |= ((unsigned char)data[6])<<16; uncompressedLength |= ((unsigned char)data[7])<<24; - char * res; - res = (char *)malloc(uncompressedLength); - if (!res) - { - error = String::Build("Unable to allocate ", uncompressedLength, " bytes of memory for decompression"); - goto corrupt; - } + std::vector res(uncompressedLength); int dstate; - dstate = BZ2_bzBuffToBuffDecompress((char *)res, (unsigned *)&uncompressedLength, &data[8], data.size()-8, 0, 0); + dstate = BZ2_bzBuffToBuffDecompress(&res[0], (unsigned *)&uncompressedLength, &data[8], data.size()-8, 0, 0); if (dstate) { - error = String::Build("Unable to decompress update: ", dstate); - free(res); - goto corrupt; + return niceNotifyError(String::Build("Unable to decompress update: ", dstate)); } notifyStatus("Applying update"); notifyProgress(-1); - Client::Ref().SetPref("version.update", true); - if (update_start(res, uncompressedLength)) + prefs.Set("version.update", true); + if (!Platform::UpdateStart(res)) { - Client::Ref().SetPref("version.update", false); - update_cleanup(); + prefs.Set("version.update", false); + Platform::UpdateCleanup(); notifyError("Update failed - try downloading a new version."); return false; } return true; - - corrupt: - notifyError("Downloaded update is corrupted\n" + error); - return false; } }; -UpdateActivity::UpdateActivity() { - ByteString file; -#ifdef UPDATESERVER - file = ByteString::Build(SCHEME, UPDATESERVER, Client::Ref().GetUpdateInfo().File); -#else - file = ByteString::Build(SCHEME, SERVER, Client::Ref().GetUpdateInfo().File); -#endif - updateDownloadTask = new UpdateDownloadTask(file, this); +UpdateActivity::UpdateActivity(UpdateInfo info) +{ + updateDownloadTask = new UpdateDownloadTask(info.file, this); updateWindow = new TaskWindow("Downloading update...", updateDownloadTask, true); } @@ -145,21 +138,25 @@ void UpdateActivity::Exit() void UpdateActivity::NotifyError(Task * sender) { -#ifdef UPDATESERVER -# define FIRST_LINE "Please go online to manually download a newer version.\n" -#else -# define FIRST_LINE "Please visit the website to download a newer version.\n" -#endif - new ConfirmPrompt("Autoupdate failed", FIRST_LINE "Error: " + sender->GetError(), { [this] { -#ifndef UPDATESERVER - Platform::OpenURI(SCHEME "powdertoy.co.uk/Download.html"); -#endif + StringBuilder sb; + if constexpr (USE_UPDATESERVER) + { + sb << "Please go online to manually download a newer version.\n"; + } + else + { + sb << "Please visit the website to download a newer version.\n"; + } + sb << "Error: " << sender->GetError(); + new ConfirmPrompt("Autoupdate failed", sb.Build(), { [this] { + if constexpr (!USE_UPDATESERVER) + { + Platform::OpenURI(ByteString(SCHEME) + "powdertoy.co.uk/Download.html"); + } Exit(); }, [this] { Exit(); } }); -#undef FIRST_LINE } UpdateActivity::~UpdateActivity() { } - diff --git a/src/gui/update/UpdateActivity.h b/src/gui/update/UpdateActivity.h index 861f62408..159a270f2 100644 --- a/src/gui/update/UpdateActivity.h +++ b/src/gui/update/UpdateActivity.h @@ -1,5 +1,5 @@ -#ifndef UPDATEACTIVITY_H_ -#define UPDATEACTIVITY_H_ +#pragma once +#include "client/StartupInfo.h" class Task; class TaskWindow; @@ -8,11 +8,9 @@ class UpdateActivity Task * updateDownloadTask; TaskWindow * updateWindow; public: - UpdateActivity(); + UpdateActivity(UpdateInfo info); virtual ~UpdateActivity(); void Exit(); virtual void NotifyDone(Task * sender); virtual void NotifyError(Task * sender); }; - -#endif /* UPDATEACTIVITY_H_ */ diff --git a/src/lua/CommandInterface.cpp b/src/lua/CommandInterface.cpp index c6f5f91c2..f2d6a47fc 100644 --- a/src/lua/CommandInterface.cpp +++ b/src/lua/CommandInterface.cpp @@ -2,23 +2,26 @@ #include #include -#if !defined(WIN) || defined(__GNUC__) -#include -#endif +#include #include "Misc.h" #include "gui/game/GameModel.h" #include "simulation/Particle.h" -CommandInterface::CommandInterface(GameController * c, GameModel * m) { +CommandInterface *commandInterface = nullptr; + +CommandInterface::CommandInterface(GameController * c, GameModel * m) +{ + assert(!commandInterface); + commandInterface = this; this->m = m; this->c = c; } -/*void CommandInterface::AttachGameModel(GameModel * m) +CommandInterface::~CommandInterface() { - this->m = m; -}*/ + commandInterface = nullptr; +} int CommandInterface::Command(String command) { @@ -78,6 +81,3 @@ String CommandInterface::GetLastError() { return lastError; } - -CommandInterface::~CommandInterface() { -} diff --git a/src/lua/CommandInterface.h b/src/lua/CommandInterface.h index bfe1145fc..623471fd1 100644 --- a/src/lua/CommandInterface.h +++ b/src/lua/CommandInterface.h @@ -1,11 +1,7 @@ -#ifndef COMMANDINTERFACE_H_ -#define COMMANDINTERFACE_H_ -#include "Config.h" - +#pragma once #include "common/String.h" -#include "lua/LuaEvents.h" +#include "gui/game/GameControllerEvents.h" -class Event; class GameModel; class GameController; class Tool; @@ -16,17 +12,19 @@ protected: String lastError; GameModel * m; GameController * c; + CommandInterface(GameController * c, GameModel * m); + public: enum LogType { LogError, LogWarning, LogNotice }; enum FormatType { FormatInt, FormatString, FormatChar, FormatFloat, FormatElement }; - CommandInterface(GameController * c, GameModel * m); int GetPropertyOffset(ByteString key, FormatType & format); void Log(LogType type, String message); //void AttachGameModel(GameModel * m); virtual void OnTick() { } + virtual void Init() { } - virtual bool HandleEvent(LuaEvents::EventTypes eventType, Event * event) { return true; } + virtual bool HandleEvent(const GameControllerEvent &event) { return true; } virtual int Command(String command); virtual String FormatCommand(String command); @@ -36,6 +34,8 @@ public: } String GetLastError(); virtual ~CommandInterface(); + + static CommandInterface *Create(GameController * c, GameModel * m); }; -#endif /* COMMANDINTERFACE_H_ */ +extern CommandInterface *commandInterface; diff --git a/src/lua/LegacyLuaAPI.cpp b/src/lua/LegacyLuaAPI.cpp index fd7e271de..be23d81cb 100644 --- a/src/lua/LegacyLuaAPI.cpp +++ b/src/lua/LegacyLuaAPI.cpp @@ -1,28 +1,16 @@ -#include "Config.h" -#ifdef LUACONSOLE - -#include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows - -#include -#include -#include -#include - #include "Format.h" #include "LuaScriptHelper.h" #include "LuaScriptInterface.h" #include "LuaSmartRef.h" -#include "PowderToy.h" - -#include "client/Client.h" -#include "common/Platform.h" +#include "PowderToySDL.h" +#include "prefs/GlobalPrefs.h" +#include "common/platform/Platform.h" #include "graphics/Graphics.h" #include "graphics/Renderer.h" #include "simulation/ElementCommon.h" -#include "simulation/Gravity.h" +#include "simulation/gravity/Gravity.h" #include "simulation/Simulation.h" #include "simulation/SimulationData.h" - #include "gui/dialogues/ConfirmPrompt.h" #include "gui/dialogues/ErrorMessage.h" #include "gui/dialogues/InformationMessage.h" @@ -30,7 +18,10 @@ #include "gui/game/GameController.h" #include "gui/game/GameModel.h" #include "gui/interface/Engine.h" -#include "gui/interface/Keys.h" +#include +#include +#include +#include std::map legacyPropNames; std::map legacyTransitionNames; @@ -43,6 +34,8 @@ void initLegacyProps() legacyPropNames.insert(std::pair("menu", prop)); else if (prop.Name == "PhotonReflectWavelengths") continue; + else if (prop.Name == "CarriesTypeIn") + continue; else if (prop.Name == "Temperature") legacyPropNames.insert(std::pair("heat", prop)); else if (prop.Name == "HeatConduct") @@ -77,68 +70,70 @@ void initLegacyProps() #ifndef FFI int luacon_partread(lua_State* l) { - int tempinteger, i = cIndex; - float tempfloat; - ByteString key = tpt_lua_optByteString(l, 2, ""); - CommandInterface::FormatType format; - int offset = luacon_ci->GetPropertyOffset(key, format); - + int i = cIndex; if (i < 0 || i >= NPART) return luaL_error(l, "Out of range"); - if (offset == -1) - { - if (byteStringEqualsLiteral(key, "id")) - { - lua_pushnumber(l, i); - return 1; - } - return luaL_error(l, "Invalid property"); - } + if (!luacon_sim->parts[i].type) + return luaL_error(l, "Dead particle"); - switch(format) + auto &properties = Particle::GetProperties(); + auto prop = properties.end(); + + ByteString fieldName = tpt_lua_toByteString(l, 2); + if (fieldName == "id") { - case CommandInterface::FormatInt: - case CommandInterface::FormatElement: - tempinteger = *((int*)(((unsigned char*)&luacon_sim->parts[i])+offset)); - lua_pushnumber(l, tempinteger); - break; - case CommandInterface::FormatFloat: - tempfloat = *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)); - lua_pushnumber(l, tempfloat); - break; - default: - break; + lua_pushnumber(l, i); + return 1; } + for (auto &alias : Particle::GetPropertyAliases()) + { + if (fieldName == alias.from) + { + fieldName = alias.to; + } + } + prop = std::find_if(properties.begin(), properties.end(), [&fieldName](StructProperty const &p) { + return p.Name == fieldName; + }); + if (prop == properties.end()) + return luaL_error(l, "Invalid property"); + + //Calculate memory address of property + intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->parts[i]) + prop->Offset); + + LuaScriptInterface::LuaGetProperty(l, *prop, propertyAddress); return 1; } int luacon_partwrite(lua_State* l) { int i = cIndex; - ByteString key = tpt_lua_optByteString(l, 2, ""); - CommandInterface::FormatType format; - int offset = luacon_ci->GetPropertyOffset(key, format); - if (i < 0 || i >= NPART) return luaL_error(l, "Out of range"); if (!luacon_sim->parts[i].type) return luaL_error(l, "Dead particle"); - if (offset == -1) - return luaL_error(l, "Invalid property"); - switch(format) + auto &properties = Particle::GetProperties(); + auto prop = properties.end(); + + ByteString fieldName = tpt_lua_toByteString(l, 2); + for (auto &alias : Particle::GetPropertyAliases()) { - case CommandInterface::FormatInt: - *((int*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = luaL_optinteger(l, 3, 0); - break; - case CommandInterface::FormatFloat: - *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = luaL_optnumber(l, 3, 0); - break; - case CommandInterface::FormatElement: - luacon_sim->part_change_type(i, int(luacon_sim->parts[i].x + 0.5f), int(luacon_sim->parts[i].y + 0.5f), luaL_optinteger(l, 3, 0)); - default: - break; + if (fieldName == alias.from) + { + fieldName = alias.to; + } } + prop = std::find_if(properties.begin(), properties.end(), [&fieldName](StructProperty const &p) { + return p.Name == fieldName; + }); + if (prop == properties.end()) + return luaL_error(l, "Invalid property"); + + //Calculate memory address of property + intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->parts[i]) + prop->Offset); + + LuaScriptInterface::LuaSetParticleProperty(l, i, *prop, propertyAddress, 3); return 0; } @@ -254,35 +249,30 @@ int luacon_elementwrite(lua_State* l) return luaL_error(l, "Invalid index"); } - if (prop.Name == "type") // i.e. it's .type - { - luacon_sim->part_change_type(i, int(luacon_sim->parts[i].x+0.5f), int(luacon_sim->parts[i].y+0.5f), luaL_checkinteger(l, 3)); - } - else - { - intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->elements[i]) + prop.Offset); - LuaScriptInterface::LuaSetProperty(l, prop, propertyAddress, 3); - } + intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->elements[i]) + prop.Offset); + LuaScriptInterface::LuaSetProperty(l, prop, propertyAddress, 3); luacon_model->BuildMenus(); luacon_sim->init_can_move(); - std::fill(&luacon_ren->graphicscache[0], &luacon_ren->graphicscache[PT_NUM], gcache_item()); + std::fill(&luacon_ren->graphicscache[0], &luacon_ren->graphicscache[0] + PT_NUM, gcache_item()); return 0; } void luacon_hook(lua_State * l, lua_Debug * ar) { - if(ar->event == LUA_HOOKCOUNT && Platform::GetTime()-ui::Engine::Ref().LastTick() > 3000) + auto *luacon_ci = static_cast(commandInterface); + if (ar->event == LUA_HOOKCOUNT && Platform::GetTime() - luacon_ci->luaExecutionStart > 3000) { if(ConfirmPrompt::Blocking("Script not responding", "The Lua script may have stopped responding. There might be an infinite loop. Press \"Stop\" to stop it", "Stop")) luaL_error(l, "Error: Script not responding"); - ui::Engine::Ref().LastTick(Platform::GetTime()); + luacon_ci->luaExecutionStart = Platform::GetTime(); } } String luacon_geterror() { + auto *luacon_ci = static_cast(commandInterface); luaL_tostring(luacon_ci->l, -1); String err = tpt_lua_optString(luacon_ci->l, -1, "failed to execute"); lua_pop(luacon_ci->l, 1); @@ -341,7 +331,7 @@ int luatpt_drawtext(lua_State* l) if (textalpha<0) textalpha = 0; if (textalpha>255) textalpha = 255; - luacon_g->drawtext(textx, texty, string, textred, textgreen, textblue, textalpha); + luacon_g->BlendText({ textx, texty }, string, RGBA(textred, textgreen, textblue, textalpha)); return 0; } @@ -401,6 +391,7 @@ int luatpt_togglewater(lua_State* l) int luatpt_setconsole(lua_State* l) { + auto *luacon_ci = static_cast(commandInterface); int acount = lua_gettop(l); if (acount == 0) { @@ -416,6 +407,7 @@ int luatpt_setconsole(lua_State* l) int luatpt_log(lua_State* l) { + auto *luacon_ci = static_cast(commandInterface); int args = lua_gettop(l); String text; bool hasText = false; @@ -452,22 +444,22 @@ int luatpt_set_pressure(lua_State* l) float value; x1 = abs(luaL_optint(l, 1, 0)); y1 = abs(luaL_optint(l, 2, 0)); - width = abs(luaL_optint(l, 3, XRES/CELL)); - height = abs(luaL_optint(l, 4, YRES/CELL)); + width = abs(luaL_optint(l, 3, XCELLS)); + height = abs(luaL_optint(l, 4, YCELLS)); value = luaL_optnumber(l, 5, 0.0f); if(value > MAX_PRESSURE) value = MAX_PRESSURE; else if(value < MIN_PRESSURE) value = MIN_PRESSURE; - if(x1 > (XRES/CELL)-1) - x1 = (XRES/CELL)-1; - if(y1 > (YRES/CELL)-1) - y1 = (YRES/CELL)-1; - if(x1+width > (XRES/CELL)-1) - width = (XRES/CELL)-x1; - if(y1+height > (YRES/CELL)-1) - height = (YRES/CELL)-y1; + if(x1 > XCELLS-1) + x1 = XCELLS-1; + if(y1 > YCELLS-1) + y1 = YCELLS-1; + if(x1+width > XCELLS-1) + width = XCELLS-x1; + if(y1+height > YCELLS-1) + height = YCELLS-y1; for (nx = x1; nx MAX_PRESSURE) value = MAX_PRESSURE; else if(value < MIN_PRESSURE) value = MIN_PRESSURE; - if(x1 > (XRES/CELL)-1) - x1 = (XRES/CELL)-1; - if(y1 > (YRES/CELL)-1) - y1 = (YRES/CELL)-1; - if(x1+width > (XRES/CELL)-1) - width = (XRES/CELL)-x1; - if(y1+height > (YRES/CELL)-1) - height = (YRES/CELL)-y1; + if(x1 > XCELLS-1) + x1 = XCELLS-1; + if(y1 > YCELLS-1) + y1 = YCELLS-1; + if(x1+width > XCELLS-1) + width = XCELLS-x1; + if(y1+height > YCELLS-1) + height = YCELLS-y1; for (nx = x1; nxgravmap[ny*(XRES/CELL)+nx] = value; + luacon_sim->gravmap[ny*XCELLS+nx] = value; } return 0; } @@ -513,22 +505,22 @@ int luatpt_reset_gravity_field(lua_State* l) int x1, y1, width, height; x1 = abs(luaL_optint(l, 1, 0)); y1 = abs(luaL_optint(l, 2, 0)); - width = abs(luaL_optint(l, 3, XRES/CELL)); - height = abs(luaL_optint(l, 4, YRES/CELL)); - if(x1 > (XRES/CELL)-1) - x1 = (XRES/CELL)-1; - if(y1 > (YRES/CELL)-1) - y1 = (YRES/CELL)-1; - if(x1+width > (XRES/CELL)-1) - width = (XRES/CELL)-x1; - if(y1+height > (YRES/CELL)-1) - height = (YRES/CELL)-y1; + width = abs(luaL_optint(l, 3, XCELLS)); + height = abs(luaL_optint(l, 4, YCELLS)); + if(x1 > XCELLS-1) + x1 = XCELLS-1; + if(y1 > YCELLS-1) + y1 = YCELLS-1; + if(x1+width > XCELLS-1) + width = XCELLS-x1; + if(y1+height > YCELLS-1) + height = YCELLS-y1; for (nx = x1; nxgravx[ny*(XRES/CELL)+nx] = 0; - luacon_sim->gravy[ny*(XRES/CELL)+nx] = 0; - luacon_sim->gravp[ny*(XRES/CELL)+nx] = 0; + luacon_sim->gravx[ny*XCELLS+nx] = 0; + luacon_sim->gravy[ny*XCELLS+nx] = 0; + luacon_sim->gravp[ny*XCELLS+nx] = 0; } return 0; } @@ -539,16 +531,16 @@ int luatpt_reset_velocity(lua_State* l) int x1, y1, width, height; x1 = abs(luaL_optint(l, 1, 0)); y1 = abs(luaL_optint(l, 2, 0)); - width = abs(luaL_optint(l, 3, XRES/CELL)); - height = abs(luaL_optint(l, 4, YRES/CELL)); - if(x1 > (XRES/CELL)-1) - x1 = (XRES/CELL)-1; - if(y1 > (YRES/CELL)-1) - y1 = (YRES/CELL)-1; - if(x1+width > (XRES/CELL)-1) - width = (XRES/CELL)-x1; - if(y1+height > (YRES/CELL)-1) - height = (YRES/CELL)-y1; + width = abs(luaL_optint(l, 3, XCELLS)); + height = abs(luaL_optint(l, 4, YCELLS)); + if(x1 > XCELLS-1) + x1 = XCELLS-1; + if(y1 > YCELLS-1) + y1 = YCELLS-1; + if(x1+width > XCELLS-1) + width = XCELLS-x1; + if(y1+height > YCELLS-1) + height = YCELLS-y1; for (nx = x1; nx(commandInterface); int r, i, x, y, w, h, t = 0, nx, ny, partsel = 0; float f = 0; int acount = lua_gettop(l); @@ -575,6 +568,8 @@ int luatpt_set_property(lua_State* l) int offset = luacon_ci->GetPropertyOffset(prop, format); if (offset == -1) return luaL_error(l, "Invalid property '%s'", prop.c_str()); + bool isX = byteStringEqualsLiteral(prop, "x"); + bool isY = byteStringEqualsLiteral(prop, "y"); if (acount > 2) { @@ -639,6 +634,14 @@ int luatpt_set_property(lua_State* l) { if (format == CommandInterface::FormatElement) luacon_sim->part_change_type(i, nx, ny, t); + else if (isX || isY) + { + float x = luacon_sim->parts[i].x; + float y = luacon_sim->parts[i].y; + float nx = isX ? f : x; + float ny = isY ? f : y; + luacon_sim->move(i, (int)(x + 0.5f), (int)(y + 0.5f), nx, ny); + } else if(format == CommandInterface::FormatFloat) *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = f; else @@ -672,6 +675,14 @@ int luatpt_set_property(lua_State* l) if (format == CommandInterface::FormatElement) luacon_sim->part_change_type(i, int(luacon_sim->parts[i].x + 0.5f), int(luacon_sim->parts[i].y + 0.5f), t); + else if (isX || isY) + { + float x = luacon_sim->parts[i].x; + float y = luacon_sim->parts[i].y; + float nx = isX ? f : x; + float ny = isY ? f : y; + luacon_sim->move(i, (int)(x + 0.5f), (int)(y + 0.5f), nx, ny); + } else if (format == CommandInterface::FormatFloat) *((float*)(((unsigned char*)&luacon_sim->parts[i])+offset)) = f; else @@ -685,7 +696,7 @@ int luatpt_get_wallmap(lua_State* l) int x1 = abs(luaL_optint(l, 1, 0)); int y1 = abs(luaL_optint(l, 2, 0)); - if(x1 > (XRES/CELL) || y1 > (YRES/CELL)) + if(x1 > XCELLS || y1 > YCELLS) return luaL_error(l, "Out of range"); lua_pushinteger(l, luacon_sim->bmap[y1][x1]); @@ -718,10 +729,10 @@ int luatpt_set_wallmap(lua_State* l) } if (x < 0 ) x = 0 ; if (y < 0 ) y = 0 ; - if (x > XRES / CELL ) x = XRES / CELL ; - if (y > YRES / CELL ) y = YRES / CELL ; - if (w > XRES / CELL - x) w = XRES / CELL - x; - if (h > YRES / CELL - y) h = YRES / CELL - y; + if (x > XCELLS ) x = XCELLS ; + if (y > YCELLS ) y = YCELLS ; + if (w > XCELLS - x) w = XCELLS - x; + if (h > YCELLS - y) h = YCELLS - y; for (int yy = y; yy < y + h; ++yy) { for (int xx = x; xx < x + w; ++xx) @@ -746,20 +757,20 @@ int luatpt_set_elecmap(lua_State* l) x1 = abs(luaL_optint(l, 1, 0)); y1 = abs(luaL_optint(l, 2, 0)); - width = abs(luaL_optint(l, 3, XRES/CELL)); - height = abs(luaL_optint(l, 4, YRES/CELL)); + width = abs(luaL_optint(l, 3, XCELLS)); + height = abs(luaL_optint(l, 4, YCELLS)); value = luaL_optint(l, acount, 0); if(acount==5) //Draw rect { - if(x1 > (XRES/CELL)) - x1 = (XRES/CELL); - if(y1 > (YRES/CELL)) - y1 = (YRES/CELL); - if(x1+width > (XRES/CELL)) - width = (XRES/CELL)-x1; - if(y1+height > (YRES/CELL)) - height = (YRES/CELL)-y1; + if(x1 > XCELLS) + x1 = XCELLS; + if(y1 > YCELLS) + y1 = YCELLS; + if(x1+width > XCELLS) + width = XCELLS-x1; + if(y1+height > YCELLS) + height = YCELLS-y1; for (nx = x1; nx (XRES/CELL)) - x1 = (XRES/CELL); - if(y1 > (YRES/CELL)) - y1 = (YRES/CELL); + if(x1 > XCELLS) + x1 = XCELLS; + if(y1 > YCELLS) + y1 = YCELLS; luacon_sim->emap[y1][x1] = value; } return 0; @@ -782,7 +793,7 @@ int luatpt_get_elecmap(lua_State* l) int x1 = abs(luaL_optint(l, 1, 0)); int y1 = abs(luaL_optint(l, 2, 0)); - if(x1 > (XRES/CELL) || y1 > (YRES/CELL)) + if(x1 > XCELLS || y1 > YCELLS) return luaL_error(l, "Out of range"); lua_pushinteger(l, luacon_sim->emap[y1][x1]); @@ -791,6 +802,7 @@ int luatpt_get_elecmap(lua_State* l) int luatpt_get_property(lua_State* l) { + auto *luacon_ci = static_cast(commandInterface); ByteString prop = tpt_lua_optByteString(l, 1, ""); int i = luaL_optint(l, 2, 0); //x coord or particle index, depending on arguments int y = luaL_optint(l, 3, -1); @@ -878,7 +890,7 @@ int luatpt_drawpixel(lua_State* l) else if (b>255) b = 255; if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->blendpixel(x, y, r, g, b, a); + luacon_g->BlendPixel({ x, y }, RGBA(r, g, b, a)); return 0; } @@ -908,7 +920,14 @@ int luatpt_drawrect(lua_State* l) else if (b>255) b = 255; if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->drawrect(x, y, w, h, r, g, b, a); + if (a == 255) + { + luacon_g->DrawRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGB(r, g, b)); + } + else + { + luacon_g->BlendRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGBA(r, g, b, a)); + } return 0; } @@ -938,7 +957,14 @@ int luatpt_fillrect(lua_State* l) else if (b>255) b = 255; if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->fillrect(x, y, w, h, r, g, b, a); + if (a == 255) + { + luacon_g->DrawFilledRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGB(r, g, b)); + } + else + { + luacon_g->BlendFilledRect(RectSized(Vec2{ x, y }, Vec2{ w, h }), RGBA(r, g, b, a)); + } return 0; } @@ -963,14 +989,21 @@ int luatpt_drawline(lua_State* l) else if (b>255) b = 255; if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->draw_line(x1, y1, x2, y2, r, g, b, a); + if (a == 255) + { + luacon_g->DrawLine({ x1, y1 }, { x2, y2 }, RGB(r, g, b)); + } + else + { + luacon_g->BlendLine({ x1, y1 }, { x2, y2 }, RGBA(r, g, b, a)); + } return 0; } int luatpt_textwidth(lua_State* l) { auto string = tpt_lua_optString(l, 1, ""); - int strwidth = Graphics::textwidth(string); + int strwidth = Graphics::TextSize(string).X - 1; lua_pushinteger(l, strwidth); return 1; } @@ -1258,58 +1291,26 @@ int luatpt_setdrawcap(lua_State* l) return 0; } -int luatpt_getscript(lua_State* l) -{ - int scriptID = luaL_checkinteger(l, 1); - auto filename = tpt_lua_checkByteString(l, 2); - int runScript = luaL_optint(l, 3, 0); - int confirmPrompt = luaL_optint(l, 4, 1); - - ByteString url = ByteString::Build(SCHEME "starcatcher.us/scripts/main.lua?get=", scriptID); - if (confirmPrompt && !ConfirmPrompt::Blocking("Do you want to install script?", url.FromUtf8(), "Install")) - return 0; - - int ret; - ByteString scriptData = http::Request::Simple(url, &ret); - if (!scriptData.size()) - { - return luaL_error(l, "Server did not return data"); - } - if (ret != 200) - { - return luaL_error(l, http::StatusText(ret).ToUtf8().c_str()); - } - - if (scriptData.Contains("Invalid script ID")) - { - return luaL_error(l, "Invalid Script ID"); - } - - if (Platform::FileExists(filename) && confirmPrompt && !ConfirmPrompt::Blocking("File already exists, overwrite?", filename.FromUtf8(), "Overwrite")) - { - return 0; - } - if (!Platform::WriteFile(std::vector(scriptData.begin(), scriptData.end()), filename)) - { - return luaL_error(l, "Unable to write to file"); - } - if (runScript) - { - tpt_lua_dostring(l, ByteString::Build("dofile('", filename, "')")); - } - - return 0; -} - int luatpt_setwindowsize(lua_State* l) { int scale = luaL_optint(l,1,1); int kiosk = luaL_optint(l,2,0); // TODO: handle this the same way as it's handled in PowderToySDL.cpp // > maybe bind the maximum allowed scale to screen size somehow - if (scale < 1 || scale > 10) scale = 1; - if (kiosk!=1) kiosk = 0; - Client::Ref().SetPref("Scale", scale); + if (scale < 1 || scale > 10) + { + scale = 1; + } + if (kiosk!=1) + { + kiosk = 0; + } + { + auto &prefs = GlobalPrefs::Ref(); + Prefs::DeferWrite dw(prefs); + prefs.Set("Scale", scale); + prefs.Set("Fullscreen", bool(kiosk)); + } ui::Engine::Ref().SetScale(scale); ui::Engine::Ref().SetFullscreen(kiosk); return 0; @@ -1352,5 +1353,3 @@ int luatpt_perfectCircle(lua_State* l) luacon_model->SetPerfectCircle(lua_toboolean(l, 1)); return 0; } - -#endif diff --git a/src/lua/LuaBit.cpp b/src/lua/LuaBit.cpp index 5dae78d3d..d5e1a98a8 100644 --- a/src/lua/LuaBit.cpp +++ b/src/lua/LuaBit.cpp @@ -1,5 +1,3 @@ -#include "Config.h" -#ifdef LUACONSOLE /* ** Lua BitOp -- a bit operations library for Lua 5.1/5.2. ** http://bitop.luajit.org/ @@ -189,4 +187,3 @@ int luaopen_bit(lua_State *L) //#endif return 1; } -#endif diff --git a/src/lua/LuaBit.h b/src/lua/LuaBit.h index 0d001d970..5886769d4 100644 --- a/src/lua/LuaBit.h +++ b/src/lua/LuaBit.h @@ -27,7 +27,6 @@ */ #pragma once -#include "Config.h" #define LUA_BITOP_VERSION "1.0.2" diff --git a/src/lua/LuaButton.cpp b/src/lua/LuaButton.cpp index eb766bc21..2ac73bbf9 100644 --- a/src/lua/LuaButton.cpp +++ b/src/lua/LuaButton.cpp @@ -1,10 +1,5 @@ -#include "Config.h" -#ifdef LUACONSOLE - #include "LuaButton.h" - #include "LuaScriptInterface.h" - #include "gui/interface/Button.h" const char LuaButton::className[] = "Button"; @@ -78,7 +73,7 @@ void LuaButton::triggerAction() { lua_rawgeti(l, LUA_REGISTRYINDEX, actionFunction); lua_rawgeti(l, LUA_REGISTRYINDEX, owner_ref); - if (lua_pcall(l, 1, 0, 0)) + if (tpt_lua_pcall(l, 1, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -88,4 +83,3 @@ void LuaButton::triggerAction() LuaButton::~LuaButton() { } -#endif diff --git a/src/lua/LuaCheckbox.cpp b/src/lua/LuaCheckbox.cpp index 5807d4a8e..38104aa6b 100644 --- a/src/lua/LuaCheckbox.cpp +++ b/src/lua/LuaCheckbox.cpp @@ -1,10 +1,5 @@ -#include "Config.h" -#ifdef LUACONSOLE - #include "LuaCheckbox.h" - #include "LuaScriptInterface.h" - #include "gui/interface/Checkbox.h" const char LuaCheckbox::className[] = "Checkbox"; @@ -77,7 +72,7 @@ void LuaCheckbox::triggerAction() lua_rawgeti(l, LUA_REGISTRYINDEX, actionFunction); lua_rawgeti(l, LUA_REGISTRYINDEX, owner_ref); lua_pushboolean(l, checkbox->GetChecked()); - if (lua_pcall(l, 2, 0, 0)) + if (tpt_lua_pcall(l, 2, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -87,4 +82,3 @@ void LuaCheckbox::triggerAction() LuaCheckbox::~LuaCheckbox() { } -#endif diff --git a/src/lua/LuaCompat.h b/src/lua/LuaCompat.h index fdbc3d5f5..5fd87bc1a 100644 --- a/src/lua/LuaCompat.h +++ b/src/lua/LuaCompat.h @@ -1,6 +1,4 @@ -#ifndef LUAINC_H -#define LUAINC_H -#include "Config.h" +#pragma once #ifdef __cplusplus extern "C" @@ -32,5 +30,3 @@ int luaL_tostring(lua_State *L, int n); #ifdef __cplusplus } #endif - -#endif diff --git a/src/lua/LuaComponent.cpp b/src/lua/LuaComponent.cpp index 886771f50..9edad894e 100644 --- a/src/lua/LuaComponent.cpp +++ b/src/lua/LuaComponent.cpp @@ -1,11 +1,6 @@ -#include "Config.h" -#ifdef LUACONSOLE - #include "LuaComponent.h" - #include "LuaScriptInterface.h" #include "LuaWindow.h" - #include "gui/interface/Component.h" #include "gui/interface/Window.h" @@ -94,4 +89,3 @@ LuaComponent::~LuaComponent() delete component; } } -#endif diff --git a/src/lua/LuaEvents.cpp b/src/lua/LuaEvents.cpp deleted file mode 100644 index 495f85cde..000000000 --- a/src/lua/LuaEvents.cpp +++ /dev/null @@ -1,252 +0,0 @@ -#include "LuaEvents.h" -#ifdef LUACONSOLE -# include "LuaCompat.h" -# include "LuaScriptInterface.h" -# include "common/Platform.h" -# include "gui/interface/Engine.h" -#endif - -void Event::PushInteger(lua_State * l, int num) -{ -#ifdef LUACONSOLE - lua_pushinteger(l, num); -#endif -} - -void Event::PushBoolean(lua_State * l, bool flag) -{ -#ifdef LUACONSOLE - lua_pushboolean(l, flag); -#endif -} - -void Event::PushString(lua_State * l, ByteString str) -{ -#ifdef LUACONSOLE - tpt_lua_pushByteString(l, str); -#endif -} - -TextInputEvent::TextInputEvent(String text): - text(text) -{} - -int TextInputEvent::PushToStack(lua_State * l) -{ - PushString(l, text.ToUtf8()); - return 1; -} - -TextEditingEvent::TextEditingEvent(String text): - text(text) -{} - -int TextEditingEvent::PushToStack(lua_State * l) -{ - PushString(l, text.ToUtf8()); - return 1; -} - -KeyEvent::KeyEvent(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt): - key(key), - scan(scan), - repeat(repeat), - shift(shift), - ctrl(ctrl), - alt(alt) -{} - -int KeyEvent::PushToStack(lua_State * l) -{ - PushInteger(l, key); - PushInteger(l, scan); - PushBoolean(l, repeat); - PushBoolean(l, shift); - PushBoolean(l, ctrl); - PushBoolean(l, alt); - - return 6; -} - -MouseDownEvent::MouseDownEvent(int x, int y, int button): - x(x), - y(y), - button(button) -{} - -int MouseDownEvent::PushToStack(lua_State * l) -{ - PushInteger(l, x); - PushInteger(l, y); - PushInteger(l, button); - - return 3; -} - -MouseUpEvent::MouseUpEvent(int x, int y, int button, int reason): - x(x), - y(y), - button(button), - reason(reason) -{} - -int MouseUpEvent::PushToStack(lua_State * l) -{ - PushInteger(l, x); - PushInteger(l, y); - PushInteger(l, button); - PushInteger(l, reason); - - return 4; -} - -MouseMoveEvent::MouseMoveEvent(int x, int y, int dx, int dy): - x(x), - y(y), - dx(dx), - dy(dy) -{} - -int MouseMoveEvent::PushToStack(lua_State * l) -{ - PushInteger(l, x); - PushInteger(l, y); - PushInteger(l, dx); - PushInteger(l, dy); - - return 4; -} - -MouseWheelEvent::MouseWheelEvent(int x, int y, int d): - x(x), - y(y), - d(d) -{} - -int MouseWheelEvent::PushToStack(lua_State * l) -{ - PushInteger(l, x); - PushInteger(l, y); - PushInteger(l, d); - - return 3; -} - -#ifdef LUACONSOLE -int LuaEvents::RegisterEventHook(lua_State *l, ByteString eventName) -{ - if (lua_isfunction(l, 2)) - { - tpt_lua_pushByteString(l, eventName); - lua_rawget(l, LUA_REGISTRYINDEX); - if (!lua_istable(l, -1)) - { - lua_pop(l, 1); - lua_newtable(l); - tpt_lua_pushByteString(l, eventName); - lua_pushvalue(l, -2); - lua_rawset(l, LUA_REGISTRYINDEX); - } - int c = lua_objlen(l, -1); - lua_pushvalue(l, 2); - lua_rawseti(l, -2, c + 1); - } - lua_pushvalue(l, 2); - return 1; -} - -int LuaEvents::UnregisterEventHook(lua_State *l, ByteString eventName) -{ - if (lua_isfunction(l, 2)) - { - tpt_lua_pushByteString(l, eventName); - lua_rawget(l, LUA_REGISTRYINDEX); - if (!lua_istable(l, -1)) - { - lua_pop(l, -1); - lua_newtable(l); - tpt_lua_pushByteString(l, eventName); - lua_pushvalue(l, -2); - lua_rawset(l, LUA_REGISTRYINDEX); - } - int len = lua_objlen(l, -1); - int adjust = 0; - for (int i = 1; i <= len; i++) - { - lua_rawgeti(l, -1, i + adjust); - // unregister the function - if (lua_equal(l, 2, -1)) - { - lua_pop(l, 1); - adjust++; - i--; - } - // Update the function index in the table if we've removed a previous function - else if (adjust) - lua_rawseti(l, -2, i); - else - lua_pop(l, 1); - } - } - return 0; -} - -bool LuaEvents::HandleEvent(LuaScriptInterface *luacon_ci, Event *event, ByteString eventName) -{ - ui::Engine::Ref().LastTick(Platform::GetTime()); - bool cont = true; - lua_State* l = luacon_ci->l; - tpt_lua_pushByteString(l, eventName); - lua_rawget(l, LUA_REGISTRYINDEX); - if (!lua_istable(l, -1)) - { - lua_pop(l, 1); - lua_newtable(l); - tpt_lua_pushByteString(l, eventName); - lua_pushvalue(l, -2); - lua_rawset(l, LUA_REGISTRYINDEX); - } - int len = lua_objlen(l, -1); - for (int i = 1; i <= len && cont; i++) - { - lua_rawgeti(l, -1, i); - int numArgs = event->PushToStack(l); - int callret = lua_pcall(l, numArgs, 1, 0); - if (callret) - { - if (luacon_geterror(luacon_ci) == "Error: Script not responding") - { - ui::Engine::Ref().LastTick(Platform::GetTime()); - for (int j = i; j <= len - 1; j++) - { - lua_rawgeti(l, -2, j + 1); - lua_rawseti(l, -3, j); - } - lua_pushnil(l); - lua_rawseti(l, -3, len); - i--; - } - luacon_ci->Log(CommandInterface::LogError, luacon_geterror(luacon_ci)); - lua_pop(l, 1); - } - else - { - if (!lua_isnoneornil(l, -1)) - cont = lua_toboolean(l, -1); - lua_pop(l, 1); - } - len = lua_objlen(l, -1); - } - lua_pop(l, 1); - return cont; -} - - -String LuaEvents::luacon_geterror(LuaScriptInterface * luacon_ci) -{ - luaL_tostring(luacon_ci->l, -1); - String err = tpt_lua_optString(luacon_ci->l, -1, "failed to execute"); - lua_pop(luacon_ci->l, 1); - return err; -} -#endif diff --git a/src/lua/LuaEvents.h b/src/lua/LuaEvents.h deleted file mode 100644 index c1436c161..000000000 --- a/src/lua/LuaEvents.h +++ /dev/null @@ -1,163 +0,0 @@ -#ifndef LUAEVENTS_H -#define LUAEVENTS_H -#include "Config.h" - -#include "common/String.h" - -struct lua_State; -class LuaScriptInterface; - -class Event -{ -protected: - void PushInteger(lua_State * l, int num); - void PushBoolean(lua_State * l, bool flag); - void PushString(lua_State * l, ByteString str); - -public: - virtual int PushToStack(lua_State * l) = 0; - virtual ~Event() = default; -}; - -class TextInputEvent : public Event -{ - String text; - -public: - TextInputEvent(String text); - - int PushToStack(lua_State * l) override; -}; - -class TextEditingEvent : public Event -{ - String text; - -public: - TextEditingEvent(String text); - - int PushToStack(lua_State * l) override; -}; - -class KeyEvent : public Event -{ - int key; - int scan; - bool repeat; - bool shift; - bool ctrl; - bool alt; - -public: - KeyEvent(int key, int scan, bool repeat, bool shift, bool ctrl, bool alt); - - int PushToStack(lua_State * l) override; -}; - -class MouseDownEvent : public Event -{ - int x; - int y; - int button; - -public: - MouseDownEvent(int x, int y, int button); - - int PushToStack(lua_State * l) override; -}; - -class MouseUpEvent : public Event -{ - int x; - int y; - int button; - int reason; - -public: - MouseUpEvent(int x, int y, int button, int reason); - - int PushToStack(lua_State * l) override; -}; - -class MouseMoveEvent : public Event -{ - int x; - int y; - int dx; - int dy; - -public: - MouseMoveEvent(int x, int y, int dx, int dy); - - int PushToStack(lua_State * l) override; -}; - -class MouseWheelEvent : public Event -{ - int x; - int y; - int d; - -public: - MouseWheelEvent(int x, int y, int d); - - int PushToStack(lua_State * l) override; -}; - -class TickEvent : public Event -{ -public: - int PushToStack(lua_State *l) override { return 0; } -}; - -class BlurEvent : public Event -{ -public: - int PushToStack(lua_State *l) override { return 0; } -}; - -class CloseEvent : public Event -{ -public: - int PushToStack(lua_State *l) override { return 0; } -}; - -class BeforeSimEvent : public Event -{ -public: - int PushToStack(lua_State *l) override { return 0; } -}; - -class AfterSimEvent : public Event -{ -public: - int PushToStack(lua_State *l) override { return 0; } -}; - -class LuaEvents -{ -public: - enum EventTypes { - keypress, - keyrelease, - textinput, - textediting, - mousedown, - mouseup, - mousemove, - mousewheel, - tick, - blur, - close, - beforesim, - aftersim, - }; - - static int RegisterEventHook(lua_State *l, ByteString eventName); - static int UnregisterEventHook(lua_State *l, ByteString eventName); - static bool HandleEvent(LuaScriptInterface *luacon_ci, Event *event, ByteString eventName); - - static String luacon_geterror(LuaScriptInterface *luacon_ci); -}; - -#endif // LUAEVENTS_H diff --git a/src/lua/LuaHttp.cpp b/src/lua/LuaHttp.cpp new file mode 100644 index 000000000..4f619d847 --- /dev/null +++ b/src/lua/LuaHttp.cpp @@ -0,0 +1,329 @@ +#include "LuaHttp.h" +#include "client/http/Request.h" +#include "client/Client.h" +#include "json/json.h" +#include "LuaScriptInterface.h" +#include "Format.h" +#include "Config.h" +#include +#include + +class RequestHandle +{ +public: + enum RequestType + { + normal, + getAuthToken, + }; + +private: + std::unique_ptr request; + bool dead = false; + RequestType type; + + RequestHandle() = default; + + std::pair FinishGetAuthToken(ByteString data) + { + std::istringstream ss(data); + Json::Value root; + try + { + ss >> root; + auto status = root["Status"].asString(); + if (status == "OK") + { + return { 200, root["Token"].asString() }; + } + return { 403, status }; + } + catch (std::exception &e) + { + std::cerr << "bad auth response: " << e.what() << std::endl; + } + return { 600, {} }; + } + +public: + static int Make(lua_State *l, const ByteString &uri, bool isPost, const ByteString &verb, RequestType type, const http::PostData &postData, const std::vector &headers) + { + auto authUser = Client::Ref().GetAuthUser(); + if (type == getAuthToken && !authUser.UserID) + { + lua_pushnil(l); + lua_pushliteral(l, "not authenticated"); + return 2; + } + auto *rh = (RequestHandle *)lua_newuserdata(l, sizeof(RequestHandle)); + if (!rh) + { + return 0; + } + new(rh) RequestHandle(); + rh->type = type; + rh->request = std::make_unique(uri); + if (verb.size()) + { + rh->request->Verb(verb); + } + for (auto &header : headers) + { + rh->request->AddHeader(header); + } + if (isPost) + { + rh->request->AddPostData(postData); + } + if (type == getAuthToken) + { + rh->request->AuthHeaders(ByteString::Build(authUser.UserID), authUser.SessionID); + } + rh->request->Start(); + luaL_newmetatable(l, "HTTPRequest"); + lua_setmetatable(l, -2); + return 1; + } + + ~RequestHandle() + { + if (!Dead()) + { + Cancel(); + } + } + + bool Dead() const + { + return dead; + } + + bool Done() const + { + return request->CheckDone(); + } + + void Progress(int *total, int *done) + { + if (!dead) + { + std::tie(*total, *done) = request->CheckProgress(); + } + } + + void Cancel() + { + if (!dead) + { + request.reset(); + dead = true; + } + } + + std::pair Finish(std::vector &headers) + { + int status = 0; + ByteString data; + if (!dead) + { + if (request->CheckDone()) + { + if (type != getAuthToken) + { + headers = request->ResponseHeaders(); + } + // Get this separately so it's always present. + status = request->StatusCode(); + try + { + data = request->Finish().second; + } + catch (const http::RequestError &ex) + { + // Nothing, the only way to fail here is to fail in RequestManager, and + // that means the problem has already been printed to std::cerr. + } + request.reset(); + if (type == getAuthToken) + { + if (status == 200) + { + std::tie(status, data) = FinishGetAuthToken(data); + } + } + dead = true; + } + } + return { status, data }; + } +}; + +static int http_request_gc(lua_State *l) +{ + auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); + rh->~RequestHandle(); + return 0; +} + +static int http_request_status(lua_State *l) +{ + auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); + if (rh->Dead()) + { + lua_pushliteral(l, "dead"); + } + else if (rh->Done()) + { + lua_pushliteral(l, "done"); + } + else + { + lua_pushliteral(l, "running"); + } + return 1; +} + +static int http_request_progress(lua_State *l) +{ + auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); + if (!rh->Dead()) + { + int total, done; + rh->Progress(&total, &done); + lua_pushinteger(l, total); + lua_pushinteger(l, done); + return 2; + } + return 0; +} + +static int http_request_cancel(lua_State *l) +{ + auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); + if (!rh->Dead()) + { + rh->Cancel(); + } + return 0; +} + +static int http_request_finish(lua_State *l) +{ + auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); + if (!rh->Dead()) + { + std::vector headers; + auto [ status, data ] = rh->Finish(headers); + tpt_lua_pushByteString(l, data); + lua_pushinteger(l, status); + lua_newtable(l); + for (auto i = 0; i < int(headers.size()); ++i) + { + lua_pushlstring(l, headers[i].data(), headers[i].size()); + lua_rawseti(l, -2, i + 1); + } + return 3; + } + return 0; +} + +static int http_request(lua_State *l, bool isPost) +{ + ByteString uri = tpt_lua_checkByteString(l, 1); + http::PostData postData; + auto headersIndex = 2; + auto verbIndex = 3; + + if (isPost) + { + headersIndex += 1; + verbIndex += 1; + if (lua_isstring(l, 2)) + { + postData = tpt_lua_toByteString(l, 2); + } + else if (lua_istable(l, 2)) + { + postData = http::FormData{}; + auto &formData = std::get(postData); + lua_pushnil(l); + while (lua_next(l, 2)) + { + lua_pushvalue(l, -2); + formData.emplace(tpt_lua_toByteString(l, -1), tpt_lua_toByteString(l, -2)); + lua_pop(l, 2); + } + } + } + + std::vector headers; + if (lua_istable(l, headersIndex)) + { + auto size = lua_objlen(l, headersIndex); + if (size) + { + for (auto i = 0U; i < size; ++i) + { + lua_rawgeti(l, headersIndex, i + 1); + headers.push_back(tpt_lua_toByteString(l, -1)); + lua_pop(l, 1); + } + } + else + { + // old dictionary format + lua_pushnil(l); + while (lua_next(l, headersIndex)) + { + lua_pushvalue(l, -2); + headers.push_back(tpt_lua_toByteString(l, -1) + ByteString(": ") + tpt_lua_toByteString(l, -2)); + lua_pop(l, 2); + } + } + } + + auto verb = tpt_lua_optByteString(l, verbIndex, ""); + return RequestHandle::Make(l, uri, isPost, verb, RequestHandle::normal, postData, headers); +} + +static int http_get_auth_token(lua_State *l) +{ + return RequestHandle::Make(l, ByteString::Build(SCHEME, SERVER, "/ExternalAuth.api?Action=Get&Audience=", format::URLEncode(tpt_lua_checkByteString(l, 1))), false, {}, RequestHandle::getAuthToken, {}, {}); +} + +static int http_get(lua_State * l) +{ + return http_request(l, false); +} + +static int http_post(lua_State * l) +{ + return http_request(l, true); +} + +void LuaHttp::Open(lua_State *l) +{ + luaL_newmetatable(l, "HTTPRequest"); + lua_pushcfunction(l, http_request_gc); + lua_setfield(l, -2, "__gc"); + lua_newtable(l); + struct luaL_Reg httpRequestIndexMethods[] = { + { "status", http_request_status }, + { "progress", http_request_progress }, + { "cancel", http_request_cancel }, + { "finish", http_request_finish }, + { NULL, NULL } + }; + luaL_register(l, NULL, httpRequestIndexMethods); + lua_setfield(l, -2, "__index"); + lua_pop(l, 1); + lua_newtable(l); + struct luaL_Reg httpMethods[] = { + { "get", http_get }, + { "post", http_post }, + { "getAuthToken", http_get_auth_token }, + { NULL, NULL } + }; + luaL_register(l, NULL, httpMethods); + lua_setglobal(l, "http"); +} diff --git a/src/lua/LuaTCPSocket.h b/src/lua/LuaHttp.h similarity index 59% rename from src/lua/LuaTCPSocket.h rename to src/lua/LuaHttp.h index 93d87fac5..1cceabced 100644 --- a/src/lua/LuaTCPSocket.h +++ b/src/lua/LuaHttp.h @@ -1,10 +1,7 @@ #pragma once - -#include "Config.h" - #include "LuaCompat.h" -namespace LuaTCPSocket +namespace LuaHttp { void Open(lua_State *l); } diff --git a/src/lua/LuaLabel.cpp b/src/lua/LuaLabel.cpp index 349c74e54..5ab3dacf1 100644 --- a/src/lua/LuaLabel.cpp +++ b/src/lua/LuaLabel.cpp @@ -1,10 +1,5 @@ -#include "Config.h" -#ifdef LUACONSOLE - #include "LuaScriptInterface.h" - #include "LuaLabel.h" - #include "gui/interface/Label.h" const char LuaLabel::className[] = "Label"; @@ -50,4 +45,3 @@ int LuaLabel::text(lua_State * l) LuaLabel::~LuaLabel() { } -#endif diff --git a/src/lua/LuaLuna.h b/src/lua/LuaLuna.h index 76b51e92c..9a7ec13be 100644 --- a/src/lua/LuaLuna.h +++ b/src/lua/LuaLuna.h @@ -1,6 +1,5 @@ #pragma once //http://lua-users.org/wiki/SimplerCppBinding - #include "LuaCompat.h" template class Luna @@ -152,7 +151,7 @@ private: char buff[32]; userdataType *ud = static_cast(lua_touserdata(L, 1)); T *obj = ud->pT; - sprintf(buff, "%p", obj); + snprintf(buff, sizeof(buff), "%p", obj); lua_pushfstring(L, "%s (%s)", T::className, buff); return 1; } diff --git a/src/lua/LuaProgressBar.cpp b/src/lua/LuaProgressBar.cpp index f4faba654..87e0fbcd5 100644 --- a/src/lua/LuaProgressBar.cpp +++ b/src/lua/LuaProgressBar.cpp @@ -1,10 +1,5 @@ -#include "Config.h" -#ifdef LUACONSOLE - #include "LuaProgressBar.h" - #include "LuaScriptInterface.h" - #include "gui/interface/ProgressBar.h" const char LuaProgressBar::className[] = "ProgressBar"; @@ -66,4 +61,3 @@ int LuaProgressBar::status(lua_State * l) LuaProgressBar::~LuaProgressBar() { } -#endif diff --git a/src/lua/LuaSDLKeys.h b/src/lua/LuaSDLKeys.h index a80aadc9e..2eb87d172 100644 --- a/src/lua/LuaSDLKeys.h +++ b/src/lua/LuaSDLKeys.h @@ -1,8 +1,6 @@ #pragma once -#include "Config.h" - #include "LuaCompat.h" -#include "SDLCompat.h" +#include #define SETCONST(L, NAME)\ lua_pushinteger(L, NAME);\ diff --git a/src/lua/LuaScriptHelper.h b/src/lua/LuaScriptHelper.h index 4abaae5e9..43c9b3819 100644 --- a/src/lua/LuaScriptHelper.h +++ b/src/lua/LuaScriptHelper.h @@ -1,6 +1,4 @@ -#ifndef LUASCRIPTHELPER_H_ -#define LUASCRIPTHELPER_H_ - +#pragma once #include "simulation/Particle.h" #include "simulation/ElementDefs.h" #include "common/String.h" @@ -9,14 +7,13 @@ class GameModel; class GameController; class Simulation; -class LuaScriptInterface; +class CommandInterface; class Graphics; class Renderer; extern GameModel * luacon_model; extern GameController * luacon_controller; extern Simulation * luacon_sim; -extern LuaScriptInterface * luacon_ci; extern Graphics * luacon_g; extern Renderer * luacon_ren; @@ -130,5 +127,3 @@ int luatpt_screenshot(lua_State* l); int luatpt_record(lua_State* l); int luatpt_perfectCircle(lua_State* l); - -#endif /* LUASCRIPTHELPER_H_ */ diff --git a/src/lua/LuaScriptInterface.cpp b/src/lua/LuaScriptInterface.cpp index 8d500d0b0..c197d5f2f 100644 --- a/src/lua/LuaScriptInterface.cpp +++ b/src/lua/LuaScriptInterface.cpp @@ -1,39 +1,31 @@ -#include "Config.h" -#ifdef LUACONSOLE - -#include "client/http/Request.h" // includes curl.h, needs to come first to silence a warning on windows #include "bzip2/bz2wrap.h" +#include "common/VariantIndex.h" #include "LuaScriptInterface.h" -#include -#include -#include -#include -#include - #include "Format.h" #include "LuaScriptHelper.h" #include "LuaLuna.h" #include "LuaBit.h" #include "LuaButton.h" #include "LuaCheckbox.h" -#include "LuaEvents.h" #include "LuaLabel.h" #include "LuaProgressBar.h" #include "LuaSlider.h" #include "LuaTextbox.h" #include "LuaWindow.h" -#include "LuaTCPSocket.h" +#include "LuaSocket.h" +#include "LuaHttp.h" #include "LuaSDLKeys.h" -#include "PowderToy.h" +#include "PowderToySDL.h" #include "TPTScriptInterface.h" #include "client/Client.h" #include "client/GameSave.h" #include "client/SaveFile.h" #include "client/SaveInfo.h" -#include "common/Platform.h" +#include "client/http/Request.h" +#include "common/platform/Platform.h" #include "graphics/Graphics.h" #include "graphics/Renderer.h" #include "simulation/Air.h" @@ -44,6 +36,7 @@ #include "simulation/Simulation.h" #include "simulation/ToolClasses.h" #include "simulation/SaveRenderer.h" +#include "simulation/Snapshot.h" #include "gui/interface/Window.h" #include "gui/interface/Engine.h" @@ -52,20 +45,19 @@ #include "gui/game/GameModel.h" #include "gui/game/Tool.h" #include "gui/game/Brush.h" +#include "gui/dialogues/ConfirmPrompt.h" +#include "gui/dialogues/ErrorMessage.h" +#include "gui/dialogues/InformationMessage.h" -#ifndef WIN -#include -#endif - -extern "C" -{ -#ifdef WIN -#include -#endif -#include -} #include "eventcompat.lua.h" +#include "Config.h" +#include +#include +#include +#include +#include + // idea from mniip, makes things much simpler #define SETCONST(L, NAME)\ lua_pushinteger(L, NAME);\ @@ -77,7 +69,6 @@ extern "C" GameModel * luacon_model; GameController * luacon_controller; Simulation * luacon_sim; -LuaScriptInterface * luacon_ci; Graphics * luacon_g; Renderer * luacon_ren; @@ -104,14 +95,14 @@ int atPanic(lua_State *l) int TptIndexClosure(lua_State *l) { - LuaScriptInterface *lsi = (LuaScriptInterface *)lua_touserdata(l, lua_upvalueindex(1)); - return lsi->tpt_index(l); + auto *luacon_ci = static_cast(commandInterface); + return luacon_ci->tpt_index(l); } int TptNewindexClosure(lua_State *l) { - LuaScriptInterface *lsi = (LuaScriptInterface *)lua_touserdata(l, lua_upvalueindex(1)); - return lsi->tpt_newIndex(l); + auto *luacon_ci = static_cast(commandInterface); + return luacon_ci->tpt_newIndex(l); } static int bz2_compress_wrapper(lua_State *L) @@ -176,8 +167,86 @@ static void initBZ2API(lua_State *L) lua_setglobal(L, "bz2"); } +static int osExit(lua_State *l) +{ + Platform::Exit(luaL_optinteger(l, 1, 0)); + return 0; +} + +static bool inSimEvent = false; + +static int mathRandom(lua_State *l) +{ + // only thing that matters is that the rng not be luacon_sim->rng when !inSimEvent + auto &rng = inSimEvent ? luacon_sim->rng : interfaceRng; + int lower, upper; + switch (lua_gettop(l)) + { + case 0: + lua_pushnumber(l, rng.uniform01()); + return 1; + + case 1: + lower = 1; + upper = luaL_checkinteger(l, 1); + break; + + default: + lower = luaL_checkinteger(l, 1); + upper = luaL_checkinteger(l, 2); + break; + } + if (upper < lower) + { + luaL_error(l, "interval is empty"); + } + lua_pushinteger(l, rng.between(lower, upper)); + return 1; +} + +static int mathRandomseed(lua_State *l) +{ + interfaceRng.seed(luaL_checkinteger(l, 1)); + return 0; +} + +static int simRandomseed(lua_State *l) +{ + if (lua_gettop(l)) + { + luacon_sim->rng.state({ + uint32_t(luaL_checkinteger(l, 1)) | (uint64_t(uint32_t(luaL_checkinteger(l, 2))) << 32), + uint32_t(luaL_checkinteger(l, 3)) | (uint64_t(uint32_t(luaL_checkinteger(l, 4))) << 32), + }); + return 0; + } + auto s = luacon_sim->rng.state(); + lua_pushinteger(l, s[0] & UINT32_C(0xFFFFFFFF)); + lua_pushinteger(l, (s[0] >> 32) & UINT32_C(0xFFFFFFFF)); + lua_pushinteger(l, s[1] & UINT32_C(0xFFFFFFFF)); + lua_pushinteger(l, (s[1] >> 32) & UINT32_C(0xFFFFFFFF)); + return 4; +} + +static int simHash(lua_State *l) +{ + lua_pushinteger(l, luacon_sim->CreateSnapshot()->Hash()); + return 1; +} + +static int simEnsureDeterminism(lua_State *l) +{ + if (lua_gettop(l)) + { + luacon_sim->ensureDeterminism = lua_toboolean(l, 1); + return 0; + } + lua_pushboolean(l, luacon_sim->ensureDeterminism); + return 1; +} + LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m): - CommandInterface(c, m), + TPTScriptInterface(c, m), luacon_mousex(0), luacon_mousey(0), luacon_mousebutton(0), @@ -187,7 +256,6 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m): luacon_selectedreplace(""), luacon_mousedown(false), currentCommand(false), - legacy(new TPTScriptInterface(c, m)), textInputRefcount(0) { luacon_model = m; @@ -195,7 +263,6 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m): luacon_sim = m->GetSimulation(); luacon_g = ui::Engine::Ref().g; luacon_ren = m->GetRenderer(); - luacon_ci = this; for (auto moving = 0; moving < PT_NUM; ++moving) { @@ -226,9 +293,19 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m): initPlatformAPI(); initEventAPI(); initHttpAPI(); -#ifndef NOHTTP initSocketAPI(); -#endif + + lua_getglobal(l, "os"); + lua_pushcfunction(l, osExit); + lua_setfield(l, -2, "exit"); + lua_pop(l, 1); + + lua_getglobal(l, "math"); + lua_pushcfunction(l, mathRandom); + lua_setfield(l, -2, "random"); + lua_pushcfunction(l, mathRandomseed); + lua_setfield(l, -2, "randomseed"); + lua_pop(l, 1); initBZ2API(l); @@ -318,11 +395,14 @@ LuaScriptInterface::LuaScriptInterface(GameController * c, GameModel * m): lua_setfield(l, tptPropertiesVersion, "minor"); lua_pushinteger(l, BUILD_NUM); lua_setfield(l, tptPropertiesVersion, "build"); -#if defined(SNAPSHOT) || MOD_ID > 0 - lua_pushinteger(l, SNAPSHOT_ID); -#else - lua_pushinteger(l, 0); -#endif + if constexpr (SNAPSHOT || MOD) + { + lua_pushinteger(l, SNAPSHOT_ID); + } + else + { + lua_pushinteger(l, 0); + } lua_setfield(l, tptPropertiesVersion, "snapshot"); lua_pushinteger(l, MOD_ID); lua_setfield(l, tptPropertiesVersion, "modid"); @@ -420,6 +500,14 @@ tpt.partsdata = nil"); lua_el_mode_v = std::vector(PT_NUM, 0); lua_el_mode = &lua_el_mode_v[0]; + gameControllerEventHandlers = std::vector(std::variant_size::value, l); + for (auto &ref : gameControllerEventHandlers) + { + lua_newtable(l); + ref.Assign(l, -1); + lua_pop(l, 1); + } + luaCtypeDrawHandlers = std::vector(PT_NUM, l); luaCreateHandlers = std::vector(PT_NUM, l); luaCreateAllowedHandlers = std::vector(PT_NUM, l); @@ -427,18 +515,15 @@ tpt.partsdata = nil"); //make tpt.* a metatable lua_newtable(l); - lua_pushlightuserdata(l, this); - lua_pushcclosure(l, TptIndexClosure, 1); + lua_pushcfunction(l, TptIndexClosure); lua_setfield(l, -2, "__index"); - lua_pushlightuserdata(l, this); - lua_pushcclosure(l, TptNewindexClosure, 1); + lua_pushcfunction(l, TptNewindexClosure); lua_setfield(l, -2, "__newindex"); lua_setmetatable(l, -2); initLegacyProps(); - ui::Engine::Ref().LastTick(Platform::GetTime()); - if (luaL_loadbuffer(l, (const char *)eventcompat_lua, eventcompat_lua_size, "@[built-in eventcompat.lua]") || lua_pcall(l, 0, 0, 0)) + if (luaL_loadbuffer(l, (const char *)eventcompat_lua, eventcompat_lua_size, "@[built-in eventcompat.lua]") || tpt_lua_pcall(l, 0, 0, 0, false)) { throw std::runtime_error(ByteString("failed to load built-in eventcompat: ") + tpt_lua_toByteString(l, -1)); } @@ -463,11 +548,10 @@ void LuaScriptInterface::Init() { if (Platform::FileExists("autorun.lua")) { - lua_State *l = luacon_ci->l; - if(luaL_loadfile(l, "autorun.lua") || lua_pcall(l, 0, 0, 0)) - luacon_ci->Log(CommandInterface::LogError, luacon_geterror()); + if(luaL_loadfile(l, "autorun.lua") || tpt_lua_pcall(l, 0, 0, 0, false)) + Log(CommandInterface::LogError, luacon_geterror()); else - luacon_ci->Log(CommandInterface::LogWarning, "Loaded autorun.lua"); + Log(CommandInterface::LogWarning, "Loaded autorun.lua"); } } @@ -484,17 +568,17 @@ int LuaScriptInterface::tpt_index(lua_State *l) else if (byteStringEqualsLiteral(key, "mousey")) return lua_pushnumber(l, c->GetView()->GetMousePosition().Y), 1; else if (byteStringEqualsLiteral(key, "selectedl")) - return tpt_lua_pushByteString(l, m->GetActiveTool(0)->GetIdentifier()), 1; + return tpt_lua_pushByteString(l, m->GetActiveTool(0)->Identifier), 1; else if (byteStringEqualsLiteral(key, "selectedr")) - return tpt_lua_pushByteString(l, m->GetActiveTool(1)->GetIdentifier()), 1; + return tpt_lua_pushByteString(l, m->GetActiveTool(1)->Identifier), 1; else if (byteStringEqualsLiteral(key, "selecteda")) - return tpt_lua_pushByteString(l, m->GetActiveTool(2)->GetIdentifier()), 1; + return tpt_lua_pushByteString(l, m->GetActiveTool(2)->Identifier), 1; else if (byteStringEqualsLiteral(key, "selectedreplace")) - return tpt_lua_pushByteString(l, m->GetActiveTool(3)->GetIdentifier()), 1; + return tpt_lua_pushByteString(l, m->GetActiveTool(3)->Identifier), 1; else if (byteStringEqualsLiteral(key, "brushx")) - return lua_pushnumber(l, m->GetBrush()->GetRadius().X), 1; + return lua_pushnumber(l, m->GetBrush().GetRadius().X), 1; else if (byteStringEqualsLiteral(key, "brushy")) - return lua_pushnumber(l, m->GetBrush()->GetRadius().Y), 1; + return lua_pushnumber(l, m->GetBrush().GetRadius().Y), 1; else if (byteStringEqualsLiteral(key, "brushID")) return lua_pushnumber(l, m->GetBrushID()), 1; else if (byteStringEqualsLiteral(key, "decoSpace")) @@ -545,7 +629,7 @@ int LuaScriptInterface::tpt_newIndex(lua_State *l) if (brushx < 0 || brushx >= XRES) luaL_error(l, "Invalid brush width"); - c->SetBrushSize(ui::Point(brushx, m->GetBrush()->GetRadius().Y)); + c->SetBrushSize(ui::Point(brushx, m->GetBrush().GetRadius().Y)); } else if (byteStringEqualsLiteral(key, "brushy")) { @@ -553,7 +637,7 @@ int LuaScriptInterface::tpt_newIndex(lua_State *l) if (brushy < 0 || brushy >= YRES) luaL_error(l, "Invalid brush height"); - c->SetBrushSize(ui::Point(m->GetBrush()->GetRadius().X, brushy)); + c->SetBrushSize(ui::Point(m->GetBrush().GetRadius().X, brushy)); } else if (byteStringEqualsLiteral(key, "brushID")) m->SetBrushID(luaL_checkinteger(l, 3)); @@ -602,6 +686,7 @@ void LuaScriptInterface::initInterfaceAPI() int LuaScriptInterface::interface_addComponent(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); void *opaque = nullptr; LuaComponent *luaComponent = nullptr; if ((opaque = Luna::tryGet(l, 1))) @@ -634,6 +719,7 @@ int LuaScriptInterface::interface_addComponent(lua_State * l) int LuaScriptInterface::interface_removeComponent(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); void *opaque = nullptr; LuaComponent *luaComponent = nullptr; if ((opaque = Luna::tryGet(l, 1))) @@ -667,6 +753,7 @@ int LuaScriptInterface::interface_removeComponent(lua_State * l) int LuaScriptInterface::interface_grabTextInput(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); luacon_ci->textInputRefcount += 1; luacon_controller->GetView()->DoesTextInput = luacon_ci->textInputRefcount > 0; return 0; @@ -674,6 +761,7 @@ int LuaScriptInterface::interface_grabTextInput(lua_State * l) int LuaScriptInterface::interface_dropTextInput(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); luacon_ci->textInputRefcount -= 1; luacon_controller->GetView()->DoesTextInput = luacon_ci->textInputRefcount > 0; return 0; @@ -943,6 +1031,10 @@ void LuaScriptInterface::initSimulationAPI() {"removeCustomGol", simulation_removeCustomGol}, {"lastUpdatedID", simulation_lastUpdatedID}, {"updateUpTo", simulation_updateUpTo}, + {"temperatureScale", simulation_temperatureScale}, + {"randomseed", simRandomseed}, + {"hash", simHash}, + {"ensureDeterminism", simEnsureDeterminism}, {NULL, NULL} }; luaL_register(l, "simulation", simulationAPIMethods); @@ -952,20 +1044,24 @@ void LuaScriptInterface::initSimulationAPI() lua_setglobal(l, "sim"); //Static values + SETCONST(l, CELL); + SETCONST(l, XCELLS); + SETCONST(l, YCELLS); + SETCONST(l, NCELL); SETCONST(l, XRES); SETCONST(l, YRES); - SETCONST(l, CELL); + SETCONST(l, NPART); SETCONST(l, NT); SETCONST(l, ST); - SETCONST(l, ITH); - SETCONST(l, ITL); + SETCONSTF(l, ITH); + SETCONSTF(l, ITL); SETCONSTF(l, IPH); SETCONSTF(l, IPL); SETCONST(l, PT_NUM); lua_pushinteger(l, 0); lua_setfield(l, -2, "NUM_PARTS"); - SETCONST(l, R_TEMP); - SETCONST(l, MAX_TEMP); - SETCONST(l, MIN_TEMP); + SETCONSTF(l, R_TEMP); + SETCONSTF(l, MAX_TEMP); + SETCONSTF(l, MIN_TEMP); SETCONSTF(l, MAX_PRESSURE); SETCONSTF(l, MIN_PRESSURE); @@ -1033,14 +1129,14 @@ void LuaScriptInterface::initSimulationAPI() void LuaScriptInterface::set_map(int x, int y, int width, int height, float value, int map) // A function so this won't need to be repeated many times later { int nx, ny; - if(x > (XRES/CELL)-1) - x = (XRES/CELL)-1; - if(y > (YRES/CELL)-1) - y = (YRES/CELL)-1; - if(x+width > (XRES/CELL)-1) - width = (XRES/CELL)-x; - if(y+height > (YRES/CELL)-1) - height = (YRES/CELL)-y; + if(x > XCELLS-1) + x = XCELLS-1; + if(y > YCELLS-1) + y = YCELLS-1; + if(x+width > XCELLS-1) + width = XCELLS-x; + if(y+height > YCELLS-1) + height = YCELLS-y; for (nx = x; nxvy[ny][nx] = value; else if (map == 5) - luacon_sim->gravmap[ny*XRES/CELL+nx] = value; //gravx/y don't seem to work, but this does. opposite of tpt + luacon_sim->gravmap[ny*XCELLS+nx] = value; //gravx/y don't seem to work, but this does. opposite of tpt } } @@ -1161,7 +1257,7 @@ int LuaScriptInterface::simulation_partPosition(lua_State * l) { int particleID = lua_tointeger(l, 1); int argCount = lua_gettop(l); - if(particleID < 0 || particleID >= NPART || !luacon_sim->parts[particleID].type) + if (particleID < 0 || particleID >= NPART || !luacon_sim->parts[particleID].type) { if(argCount == 1) { @@ -1173,10 +1269,12 @@ int LuaScriptInterface::simulation_partPosition(lua_State * l) } } - if(argCount == 3) + if (argCount == 3) { - luacon_sim->parts[particleID].x = lua_tonumber(l, 2); - luacon_sim->parts[particleID].y = lua_tonumber(l, 3); + float x = luacon_sim->parts[particleID].x; + float y = luacon_sim->parts[particleID].y; + luacon_sim->move(particleID, (int)(x + 0.5f), (int)(y + 0.5f), lua_tonumber(l, 2), lua_tonumber(l, 3)); + return 0; } else @@ -1193,13 +1291,15 @@ int LuaScriptInterface::simulation_partProperty(lua_State * l) int particleID = luaL_checkinteger(l, 1); StructProperty property; - if(particleID < 0 || particleID >= NPART || !luacon_sim->parts[particleID].type) + if (particleID < 0 || particleID >= NPART || !luacon_sim->parts[particleID].type) { - if(argCount == 3) + if (argCount == 3) { lua_pushnil(l); return 1; - } else { + } + else + { return 0; } } @@ -1239,16 +1339,9 @@ int LuaScriptInterface::simulation_partProperty(lua_State * l) //Calculate memory address of property intptr_t propertyAddress = (intptr_t)(((unsigned char*)&luacon_sim->parts[particleID]) + prop->Offset); - if(argCount == 3) + if (argCount == 3) { - if (prop == properties.begin() + 0) // i.e. it's .type - { - luacon_sim->part_change_type(particleID, int(luacon_sim->parts[particleID].x+0.5f), int(luacon_sim->parts[particleID].y+0.5f), luaL_checkinteger(l, 3)); - } - else - { - LuaSetProperty(l, *prop, propertyAddress, 3); - } + LuaSetParticleProperty(l, particleID, *prop, propertyAddress, 3); return 0; } else @@ -1285,7 +1378,7 @@ int LuaScriptInterface::simulation_pressure(lua_State* l) luaL_checktype(l, 2, LUA_TNUMBER); int x = lua_tointeger(l, 1); int y = lua_tointeger(l, 2); - if (x*CELL<0 || y*CELL<0 || x*CELL>=XRES || y*CELL>=YRES) + if (x<0 || y<0 || x>=XCELLS || y>=YCELLS) return luaL_error(l, "coordinates out of range (%d,%d)", x, y); if (argCount == 2) @@ -1322,7 +1415,7 @@ int LuaScriptInterface::simulation_ambientHeat(lua_State* l) luaL_checktype(l, 2, LUA_TNUMBER); int x = lua_tointeger(l, 1); int y = lua_tointeger(l, 2); - if (x*CELL<0 || y*CELL<0 || x*CELL>=XRES || y*CELL>=YRES) + if (x<0 || y<0 || x>=XCELLS || y>=YCELLS) return luaL_error(l, "coordinates out of range (%d,%d)", x, y); if (argCount == 2) @@ -1359,7 +1452,7 @@ int LuaScriptInterface::simulation_velocityX(lua_State* l) luaL_checktype(l, 2, LUA_TNUMBER); int x = lua_tointeger(l, 1); int y = lua_tointeger(l, 2); - if (x*CELL<0 || y*CELL<0 || x*CELL>=XRES || y*CELL>=YRES) + if (x<0 || y<0 || x>=XCELLS || y>=YCELLS) return luaL_error(l, "coordinates out of range (%d,%d)", x, y); if (argCount == 2) @@ -1396,7 +1489,7 @@ int LuaScriptInterface::simulation_velocityY(lua_State* l) luaL_checktype(l, 2, LUA_TNUMBER); int x = lua_tointeger(l, 1); int y = lua_tointeger(l, 2); - if (x*CELL<0 || y*CELL<0 || x*CELL>=XRES || y*CELL>=YRES) + if (x<0 || y<0 || x>=XCELLS || y>=YCELLS) return luaL_error(l, "coordinates out of range (%d,%d)", x, y); if (argCount == 2) @@ -1433,12 +1526,12 @@ int LuaScriptInterface::simulation_gravMap(lua_State* l) luaL_checktype(l, 2, LUA_TNUMBER); int x = lua_tointeger(l, 1); int y = lua_tointeger(l, 2); - if (x*CELL<0 || y*CELL<0 || x*CELL>=XRES || y*CELL>=YRES) + if (x<0 || y<0 || x>=XCELLS || y>=YCELLS) return luaL_error(l, "coordinates out of range (%d,%d)", x, y); if (argCount == 2) { - lua_pushnumber(l, luacon_sim->gravp[y*XRES/CELL+x]); + lua_pushnumber(l, luacon_sim->gravp[y*XCELLS+x]); return 1; } int width = 1, height = 1; @@ -1465,18 +1558,17 @@ int LuaScriptInterface::simulation_createParts(lua_State * l) int y = luaL_optint(l,2,-1); int rx = luaL_optint(l,3,5); int ry = luaL_optint(l,4,5); - int c = luaL_optint(l,5,luacon_model->GetActiveTool(0)->GetToolID()); - int brush = luaL_optint(l,6,CIRCLE_BRUSH); + int c = luaL_optint(l,5,luacon_model->GetActiveTool(0)->ToolID); + int brushID = luaL_optint(l,6,CIRCLE_BRUSH); int flags = luaL_optint(l,7,luacon_sim->replaceModeFlags); - std::vector brushList = luacon_model->GetBrushList(); - if (brush < 0 || brush >= (int)brushList.size()) - return luaL_error(l, "Invalid brush id '%d'", brush); - ui::Point tempRadius = brushList[brush]->GetRadius(); - brushList[brush]->SetRadius(ui::Point(rx, ry)); + Brush *brush = luacon_model->GetBrushByID(brushID); + if (!brush) + return luaL_error(l, "Invalid brush id '%d'", brushID); + auto newBrush = brush->Clone(); + newBrush->SetRadius(ui::Point(rx, ry)); - int ret = luacon_sim->CreateParts(x, y, c, brushList[brush], flags); - brushList[brush]->SetRadius(tempRadius); + int ret = luacon_sim->CreateParts(x, y, c, *newBrush, flags); lua_pushinteger(l, ret); return 1; } @@ -1489,18 +1581,17 @@ int LuaScriptInterface::simulation_createLine(lua_State * l) int y2 = luaL_optint(l,4,-1); int rx = luaL_optint(l,5,5); int ry = luaL_optint(l,6,5); - int c = luaL_optint(l,7,luacon_model->GetActiveTool(0)->GetToolID()); - int brush = luaL_optint(l,8,CIRCLE_BRUSH); + int c = luaL_optint(l,7,luacon_model->GetActiveTool(0)->ToolID); + int brushID = luaL_optint(l,8,CIRCLE_BRUSH); int flags = luaL_optint(l,9,luacon_sim->replaceModeFlags); - std::vector brushList = luacon_model->GetBrushList(); - if (brush < 0 || brush >= (int)brushList.size()) - return luaL_error(l, "Invalid brush id '%d'", brush); - ui::Point tempRadius = brushList[brush]->GetRadius(); - brushList[brush]->SetRadius(ui::Point(rx, ry)); + Brush *brush = luacon_model->GetBrushByID(brushID); + if (!brush) + return luaL_error(l, "Invalid brush id '%d'", brushID); + auto newBrush = brush->Clone(); + newBrush->SetRadius(ui::Point(rx, ry)); - luacon_sim->CreateLine(x1, y1, x2, y2, c, brushList[brush], flags); - brushList[brush]->SetRadius(tempRadius); + luacon_sim->CreateLine(x1, y1, x2, y2, c, *newBrush, flags); return 0; } @@ -1510,7 +1601,7 @@ int LuaScriptInterface::simulation_createBox(lua_State * l) int y1 = luaL_optint(l,2,-1); int x2 = luaL_optint(l,3,-1); int y2 = luaL_optint(l,4,-1); - int c = luaL_optint(l,5,luacon_model->GetActiveTool(0)->GetToolID()); + int c = luaL_optint(l,5,luacon_model->GetActiveTool(0)->ToolID); int flags = luaL_optint(l,6,luacon_sim->replaceModeFlags); luacon_sim->CreateBox(x1, y1, x2, y2, c, flags); @@ -1521,13 +1612,13 @@ int LuaScriptInterface::simulation_floodParts(lua_State * l) { int x = luaL_optint(l,1,-1); int y = luaL_optint(l,2,-1); - int c = luaL_optint(l,3,luacon_model->GetActiveTool(0)->GetToolID()); + int c = luaL_optint(l,3,luacon_model->GetActiveTool(0)->ToolID); int cm = luaL_optint(l,4,-1); int flags = luaL_optint(l,5,luacon_sim->replaceModeFlags); - + if (x < 0 || x >= XRES || y < 0 || y >= YRES) return luaL_error(l, "coordinates out of range (%d,%d)", x, y); - + int ret = luacon_sim->FloodParts(x, y, c, cm, flags); lua_pushinteger(l, ret); return 1; @@ -1614,7 +1705,7 @@ int LuaScriptInterface::simulation_toolBrush(lua_State * l) int rx = luaL_optint(l,3,5); int ry = luaL_optint(l,4,5); int tool = luaL_optint(l,5,0); - int brush = luaL_optint(l,6,CIRCLE_BRUSH); + int brushID = luaL_optint(l,6,CIRCLE_BRUSH); float strength = luaL_optnumber(l,7,1.0f); if (tool == (int)luacon_sim->tools.size()) { @@ -1624,14 +1715,13 @@ int LuaScriptInterface::simulation_toolBrush(lua_State * l) else if (tool < 0 || tool > (int)luacon_sim->tools.size()) return luaL_error(l, "Invalid tool id '%d'", tool); - std::vector brushList = luacon_model->GetBrushList(); - if (brush < 0 || brush >= (int)brushList.size()) - return luaL_error(l, "Invalid brush id '%d'", brush); - ui::Point tempRadius = brushList[brush]->GetRadius(); - brushList[brush]->SetRadius(ui::Point(rx, ry)); + Brush *brush = luacon_model->GetBrushByID(brushID); + if (!brush) + return luaL_error(l, "Invalid brush id '%d'", brushID); + auto newBrush = brush->Clone(); + newBrush->SetRadius(ui::Point(rx, ry)); - int ret = luacon_sim->ToolBrush(x, y, tool, brushList[brush], strength); - brushList[brush]->SetRadius(tempRadius); + int ret = luacon_sim->ToolBrush(x, y, tool, *newBrush, strength); lua_pushinteger(l, ret); return 1; } @@ -1645,31 +1735,30 @@ int LuaScriptInterface::simulation_toolLine(lua_State * l) int rx = luaL_optint(l,5,5); int ry = luaL_optint(l,6,5); int tool = luaL_optint(l,7,0); - int brush = luaL_optint(l,8,CIRCLE_BRUSH); + int brushID = luaL_optint(l,8,CIRCLE_BRUSH); float strength = luaL_optnumber(l,9,1.0f); - + if (x1 < 0 || x2 < 0 || x1 >= XRES || x2 >= XRES || y1 < 0 || y2 < 0 || y1 >= YRES || y2 >= YRES) return luaL_error(l, "coordinates out of range (%d,%d),(%d,%d)", x1, y1, x2, y2); if (tool < 0 || tool >= (int)luacon_sim->tools.size()+1) return luaL_error(l, "Invalid tool id '%d'", tool); - std::vector brushList = luacon_model->GetBrushList(); - if (brush < 0 || brush >= (int)brushList.size()) - return luaL_error(l, "Invalid brush id '%d'", brush); - ui::Point tempRadius = brushList[brush]->GetRadius(); - brushList[brush]->SetRadius(ui::Point(rx, ry)); + Brush *brush = luacon_model->GetBrushByID(brushID); + if (!brush) + return luaL_error(l, "Invalid brush id '%d'", brushID); + auto newBrush = brush->Clone(); + newBrush->SetRadius(ui::Point(rx, ry)); if (tool == (int)luacon_sim->tools.size()) { Tool *windTool = luacon_model->GetToolFromIdentifier("DEFAULT_UI_WIND"); - float oldStrength = windTool->GetStrength(); - windTool->SetStrength(strength); - windTool->DrawLine(luacon_sim, brushList[brush], ui::Point(x1, y1), ui::Point(x2, y2)); - windTool->SetStrength(oldStrength); + float oldStrength = windTool->Strength; + windTool->Strength = strength; + windTool->DrawLine(luacon_sim, *newBrush, ui::Point(x1, y1), ui::Point(x2, y2)); + windTool->Strength = oldStrength; } else - luacon_sim->ToolLine(x1, y1, x2, y2, tool, brushList[brush], strength); - brushList[brush]->SetRadius(tempRadius); + luacon_sim->ToolLine(x1, y1, x2, y2, tool, *newBrush, strength); return 0; } @@ -1706,16 +1795,15 @@ int LuaScriptInterface::simulation_decoBrush(lua_State * l) int b = luaL_optint(l,7,255); int a = luaL_optint(l,8,255); int tool = luaL_optint(l,9,DECO_DRAW); - int brush = luaL_optint(l,10,CIRCLE_BRUSH); + int brushID = luaL_optint(l,10,CIRCLE_BRUSH); - std::vector brushList = luacon_model->GetBrushList(); - if (brush < 0 || brush >= (int)brushList.size()) - return luaL_error(l, "Invalid brush id '%d'", brush); - ui::Point tempRadius = brushList[brush]->GetRadius(); - brushList[brush]->SetRadius(ui::Point(rx, ry)); + Brush *brush = luacon_model->GetBrushByID(brushID); + if (!brush) + return luaL_error(l, "Invalid brush id '%d'", brushID); + auto newBrush = brush->Clone(); + newBrush->SetRadius(ui::Point(rx, ry)); - luacon_sim->ApplyDecorationPoint(x, y, r, g, b, a, tool, brushList[brush]); - brushList[brush]->SetRadius(tempRadius); + luacon_sim->ApplyDecorationPoint(x, y, r, g, b, a, tool, *newBrush); return 0; } @@ -1732,19 +1820,18 @@ int LuaScriptInterface::simulation_decoLine(lua_State * l) int b = luaL_optint(l,9,255); int a = luaL_optint(l,10,255); int tool = luaL_optint(l,11,DECO_DRAW); - int brush = luaL_optint(l,12,CIRCLE_BRUSH); + int brushID = luaL_optint(l,12,CIRCLE_BRUSH); if (x1 < 0 || x2 < 0 || x1 >= XRES || x2 >= XRES || y1 < 0 || y2 < 0 || y1 >= YRES || y2 >= YRES) return luaL_error(l, "coordinates out of range (%d,%d),(%d,%d)", x1, y1, x2, y2); - std::vector brushList = luacon_model->GetBrushList(); - if (brush < 0 || brush >= (int)brushList.size()) - return luaL_error(l, "Invalid brush id '%d'", brush); - ui::Point tempRadius = brushList[brush]->GetRadius(); - brushList[brush]->SetRadius(ui::Point(rx, ry)); + Brush *brush = luacon_model->GetBrushByID(brushID); + if (!brush) + return luaL_error(l, "Invalid brush id '%d'", brushID); + auto newBrush = brush->Clone(); + newBrush->SetRadius(ui::Point(rx, ry)); - luacon_sim->ApplyDecorationLine(x1, y1, x2, y2, r, g, b, a, tool, brushList[brush]); - brushList[brush]->SetRadius(tempRadius); + luacon_sim->ApplyDecorationLine(x1, y1, x2, y2, r, g, b, a, tool, *newBrush); return 0; } @@ -1770,36 +1857,22 @@ int LuaScriptInterface::simulation_decoBox(lua_State * l) int LuaScriptInterface::simulation_decoColor(lua_State * l) { int acount = lua_gettop(l); - unsigned int color; + RGBA color(0, 0, 0, 0); if (acount == 0) { - ui::Colour tempColor = luacon_model->GetColourSelectorColour(); - unsigned int color = (tempColor.Alpha << 24) | PIXRGB(tempColor.Red, tempColor.Green, tempColor.Blue); - lua_pushnumber(l, color); + lua_pushnumber(l, luacon_model->GetColourSelectorColour().Pack()); return 1; } else if (acount == 1) - color = (unsigned int)luaL_optnumber(l, 1, 0xFFFF0000); + color = RGBA::Unpack(pixel_rgba(luaL_optnumber(l, 1, 0xFFFF0000))); else { - int r, g, b, a; - r = luaL_optint(l, 1, 255); - g = luaL_optint(l, 2, 255); - b = luaL_optint(l, 3, 255); - a = luaL_optint(l, 4, 255); - - if (r < 0) r = 0; - if (r > 255) r = 255; - if (g < 0) g = 0; - if (g > 255) g = 255; - if (b < 0) b = 0; - if (b > 255) b = 255; - if (a < 0) a = 0; - if (a > 255) a = 255; - - color = (a << 24) + PIXRGB(r, g, b); + color.Red = std::clamp(luaL_optint(l, 1, 255), 0, 255); + color.Green = std::clamp(luaL_optint(l, 2, 255), 0, 255); + color.Blue = std::clamp(luaL_optint(l, 3, 255), 0, 255); + color.Alpha = std::clamp(luaL_optint(l, 4, 255), 0, 255); } - luacon_model->SetColourSelectorColour(ui::Colour(PIXR(color), PIXG(color), PIXB(color), color >> 24)); + luacon_model->SetColourSelectorColour(color); return 0; } @@ -1816,8 +1889,8 @@ int LuaScriptInterface::simulation_floodDeco(lua_State * l) return luaL_error(l, "coordinates out of range (%d,%d)", x, y); // hilariously broken, intersects with console and all Lua graphics - pixel loc = luacon_ren->vid[x + y * WINDOWW]; - luacon_sim->ApplyDecorationFill(luacon_ren, x, y, r, g, b, a, PIXR(loc), PIXG(loc), PIXB(loc)); + auto loc = RGB::Unpack(luacon_ren->GetPixel({ x, y })); + luacon_sim->ApplyDecorationFill(luacon_ren, x, y, r, g, b, a, loc.Red, loc.Green, loc.Blue); return 0; } @@ -1852,27 +1925,27 @@ int LuaScriptInterface::simulation_resetTemp(lua_State * l) int LuaScriptInterface::simulation_resetPressure(lua_State * l) { - int aCount = lua_gettop(l), width = XRES/CELL, height = YRES/CELL; + int aCount = lua_gettop(l), width = XCELLS, height = YCELLS; int x1 = abs(luaL_optint(l, 1, 0)); int y1 = abs(luaL_optint(l, 2, 0)); if (aCount > 2) { - width = abs(luaL_optint(l, 3, XRES/CELL)); - height = abs(luaL_optint(l, 4, YRES/CELL)); + width = abs(luaL_optint(l, 3, XCELLS)); + height = abs(luaL_optint(l, 4, YCELLS)); } else if (aCount) { width = 1; height = 1; } - if(x1 > (XRES/CELL)-1) - x1 = (XRES/CELL)-1; - if(y1 > (YRES/CELL)-1) - y1 = (YRES/CELL)-1; - if(x1+width > (XRES/CELL)-1) - width = (XRES/CELL)-x1; - if(y1+height > (YRES/CELL)-1) - height = (YRES/CELL)-y1; + if(x1 > XCELLS-1) + x1 = XCELLS-1; + if(y1 > YCELLS-1) + y1 = YCELLS-1; + if(x1+width > XCELLS-1) + width = XCELLS-x1; + if(y1+height > YCELLS-1) + height = YCELLS-y1; for (int nx = x1; nx(commandInterface); int i = -1; int pushed = 1; - SaveFile * tempfile = NULL; - int x = luaL_optint(l,2,0); - int y = luaL_optint(l,3,0); + std::unique_ptr tempfile; + Vec2 partP = { + luaL_optint(l, 2, 0), + luaL_optint(l, 3, 0), + }; + auto hflip = lua_toboolean(l, 4); + auto rotation = luaL_optint(l, 5, 0) & 3; // [0, 3] rotations + auto &client = Client::Ref(); if (lua_isstring(l, 1)) //Load from 10 char name, or full filename { auto filename = tpt_lua_optByteString(l, 1, ""); - tempfile = Client::Ref().GetStamp(filename); + tempfile = client.GetStamp(filename); } if ((!tempfile || !tempfile->GetGameSave()) && lua_isnumber(l, 1)) //Load from stamp ID { i = luaL_optint(l, 1, 0); - int stampCount = Client::Ref().GetStampsCount(); - if (i < 0 || i >= stampCount) + auto &stampIDs = client.GetStamps(); + if (i < 0 || i >= int(stampIDs.size())) return luaL_error(l, "Invalid stamp ID: %d", i); - tempfile = Client::Ref().GetStamp(Client::Ref().GetStamps(0, stampCount)[i]); + tempfile = client.GetStamp(stampIDs[i]); } - if (tempfile) + if (tempfile && tempfile->GetGameSave()) { - if (!luacon_sim->Load(tempfile->GetGameSave(), !luacon_controller->GetView()->ShiftBehaviour(), x, y)) + auto gameSave = tempfile->TakeGameSave(); + auto [ quoX, remX ] = floorDiv(partP.X, CELL); + auto [ quoY, remY ] = floorDiv(partP.Y, CELL); + if (remX || remY || hflip || rotation) { - //luacon_sim->sys_pause = (tempfile->GetGameSave()->paused | luacon_model->GetPaused())?1:0; - lua_pushinteger(l, 1); - - if (tempfile->GetGameSave()->authors.size()) + auto transform = Mat2::Identity; + if (hflip) { - tempfile->GetGameSave()->authors["type"] = "luastamp"; - Client::Ref().MergeStampAuthorInfo(tempfile->GetGameSave()->authors); + transform = Mat2::MirrorX * transform; } + for (auto i = 0; i < rotation; ++i) + { + transform = Mat2::CCW * transform; + } + gameSave->Transform(transform, { remX, remY }); } - else + luacon_sim->Load(gameSave.get(), !luacon_controller->GetView()->ShiftBehaviour(), { quoX, quoY }); + lua_pushinteger(l, 1); + + if (gameSave->authors.size()) { - pushed = 2; - lua_pushnil(l); - tpt_lua_pushString(l, luacon_ci->GetLastError()); + gameSave->authors["type"] = "luastamp"; + client.MergeStampAuthorInfo(gameSave->authors); } - delete tempfile; } else { pushed = 2; lua_pushnil(l); - lua_pushliteral(l, "Failed to read file"); + tpt_lua_pushString(l, luacon_ci->GetLastError()); } return pushed; } int LuaScriptInterface::simulation_deleteStamp(lua_State * l) { - int stampCount = Client::Ref().GetStampsCount(); - std::vector stamps = Client::Ref().GetStamps(0, stampCount); + auto &client = Client::Ref(); + auto &stampIDs = client.GetStamps(); if (lua_isstring(l, 1)) //note: lua_isstring returns true on numbers too { auto filename = tpt_lua_optByteString(l, 1, ""); - for (auto &stamp : stamps) + for (auto &stampID : stampIDs) { - if (stamp == filename) + if (stampID == filename) { - Client::Ref().DeleteStamp(stamp); + client.DeleteStamp(stampID); return 0; } } @@ -1963,9 +2048,9 @@ int LuaScriptInterface::simulation_deleteStamp(lua_State * l) if (lua_isnumber(l, 1)) //Load from stamp ID { int i = luaL_optint(l, 1, 0); - if (i < 0 || i >= stampCount) + if (i < 0 || i >= int(stampIDs.size())) return luaL_error(l, "Invalid stamp ID: %d", i); - Client::Ref().DeleteStamp(stamps[i]); + client.DeleteStamp(stampIDs[i]); return 0; } lua_pushnumber(l, -1); @@ -1989,7 +2074,7 @@ int LuaScriptInterface::simulation_reloadSave(lua_State * l) int LuaScriptInterface::simulation_getSaveID(lua_State *l) { - SaveInfo *tempSave = luacon_model->GetSave(); + auto *tempSave = luacon_model->GetSave(); if (tempSave) { lua_pushinteger(l, tempSave->GetID()); @@ -2134,6 +2219,7 @@ int LuaScriptInterface::simulation_elementCount(lua_State * l) int LuaScriptInterface::simulation_canMove(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); int movingElement = luaL_checkint(l, 1); int destinationElement = luaL_checkint(l, 2); if (movingElement < 0 || movingElement >= PT_NUM) @@ -2160,47 +2246,18 @@ int BrushClosure(lua_State * l) // see Simulation::ToolBrush int positionX = lua_tointeger(l, lua_upvalueindex(1)); int positionY = lua_tointeger(l, lua_upvalueindex(2)); - int radiusX = lua_tointeger(l, lua_upvalueindex(3)); - int radiusY = lua_tointeger(l, lua_upvalueindex(4)); - int sizeX = lua_tointeger(l, lua_upvalueindex(5)); - int sizeY = lua_tointeger(l, lua_upvalueindex(6)); - int x = lua_tointeger(l, lua_upvalueindex(7)); - int y = lua_tointeger(l, lua_upvalueindex(8)); - unsigned char *bitmap = (unsigned char *)lua_touserdata(l, lua_upvalueindex(9)); + int i = lua_tointeger(l, lua_upvalueindex(3)); + int size = lua_tointeger(l, lua_upvalueindex(4)); + auto points = reinterpret_cast(lua_touserdata(l, lua_upvalueindex(5))); + if (i == size) + return 0; - int yield_x, yield_y; - while (true) - { - if (!(y < sizeY)) - return 0; - if (x < sizeX) - { - bool yield_coords = false; - if (bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES)) - { - yield_coords = true; - yield_x = positionX+(x-radiusX); - yield_y = positionY+(y-radiusY); - } - x++; - if (yield_coords) - break; - } - else - { - x = 0; - y++; - } - } + lua_pushnumber(l, i + 1); + lua_replace(l, lua_upvalueindex(3)); - lua_pushnumber(l, x); - lua_replace(l, lua_upvalueindex(7)); - lua_pushnumber(l, y); - lua_replace(l, lua_upvalueindex(8)); - - lua_pushnumber(l, yield_x); - lua_pushnumber(l, yield_y); + lua_pushnumber(l, points[i].X + positionX); + lua_pushnumber(l, points[i].Y + positionY); return 2; } @@ -2210,43 +2267,34 @@ int LuaScriptInterface::simulation_brush(lua_State * l) int positionX = luaL_checkint(l, 1); int positionY = luaL_checkint(l, 2); int brushradiusX, brushradiusY; - if (argCount >= 4 || !luacon_model->GetBrush()) + if (argCount >= 4) { brushradiusX = luaL_checkint(l, 3); brushradiusY = luaL_checkint(l, 4); } else { - ui::Point radius = luacon_model->GetBrush()->GetRadius(); + ui::Point radius = luacon_model->GetBrush().GetRadius(); brushradiusX = radius.X; brushradiusY = radius.Y; } int brushID = luaL_optint(l, 5, luacon_model->GetBrushID()); - std::vector brushList = luacon_model->GetBrushList(); - if (brushID < 0 || brushID >= (int)brushList.size()) + Brush *brush = luacon_model->GetBrushByID(brushID); + if (!brush) return luaL_error(l, "Invalid brush id '%d'", brushID); - - ui::Point tempRadius = brushList[brushID]->GetRadius(); - brushList[brushID]->SetRadius(ui::Point(brushradiusX, brushradiusY)); + auto newBrush = brush->Clone(); + newBrush->SetRadius(ui::Point(brushradiusX, brushradiusY)); lua_pushnumber(l, positionX); lua_pushnumber(l, positionY); - int radiusX = brushList[brushID]->GetRadius().X; - int radiusY = brushList[brushID]->GetRadius().Y; - int sizeX = brushList[brushID]->GetSize().X; - int sizeY = brushList[brushID]->GetSize().Y; - lua_pushnumber(l, radiusX); - lua_pushnumber(l, radiusY); - lua_pushnumber(l, sizeX); - lua_pushnumber(l, sizeY); - lua_pushnumber(l, 0); - lua_pushnumber(l, 0); - int bitmapSize = sizeX * sizeY * sizeof(unsigned char); - void *bitmapCopy = lua_newuserdata(l, bitmapSize); - memcpy(bitmapCopy, brushList[brushID]->GetBitmap(), bitmapSize); - brushList[brushID]->SetRadius(tempRadius); + std::vector points; + std::copy(newBrush->begin(), newBrush->end(), std::back_inserter(points)); + lua_pushnumber(l, 0); // index + lua_pushnumber(l, int(points.size())); + auto points_ud = reinterpret_cast(lua_newuserdata(l, points.size() * sizeof(ui::Point))); + std::copy(points.begin(), points.end(), points_ud); - lua_pushcclosure(l, BrushClosure, 9); + lua_pushcclosure(l, BrushClosure, 5); return 1; } @@ -2511,35 +2559,33 @@ int LuaScriptInterface::simulation_lastUpdatedID(lua_State *l) int LuaScriptInterface::simulation_updateUpTo(lua_State *l) { + // sim.updateUpTo dispatches an update to the range [current, upTo], but GameModel::UpdateUpTo takes a range [current, upTo). + // As a result, upTo here will be one smaller than it's logical for the duration of this function. int upTo = NPART - 1; if (lua_gettop(l) > 0) { upTo = luaL_checkinteger(l, 1); } - if (upTo < 0 || upTo >= NPART) + if (upTo < -1 || upTo >= NPART) // -1 instead of 0 to allow for the empty range [0, -1] aka [0, 0) { return luaL_error(l, "ID not in valid range"); } - if (upTo < luacon_sim->debug_currentParticle) + luacon_sim->framerender = 1; + luacon_model->UpdateUpTo(upTo + 1); + return 0; +} + +int LuaScriptInterface::simulation_temperatureScale(lua_State *l) +{ + if (lua_gettop(l) == 0) { - upTo = NPART - 1; - } - if (luacon_sim->debug_currentParticle == 0) - { - luacon_sim->framerender = 1; - luacon_sim->BeforeSim(); - luacon_sim->framerender = 0; - } - luacon_sim->UpdateParticles(luacon_sim->debug_currentParticle, upTo); - if (upTo < NPART - 1) - { - luacon_sim->debug_currentParticle = upTo + 1; - } - else - { - luacon_sim->AfterSim(); - luacon_sim->debug_currentParticle = 0; + lua_pushinteger(l, luacon_model->GetTemperatureScale()); + return 1; } + int temperatureScale = luaL_checkinteger(l, 1); + if (temperatureScale < 0 || temperatureScale > 2) + return luaL_error(l, "Invalid temperature scale"); + luacon_model->SetTemperatureScale(temperatureScale); return 0; } @@ -2946,11 +2992,7 @@ void LuaScriptInterface::LuaGetProperty(lua_State* l, StructProperty property, i break; } case StructProperty::Colour: -#if PIXELSIZE == 4 lua_pushinteger(l, *((unsigned int*)propertyAddress)); -#else - lua_pushinteger(l, *((unsigned short*)propertyAddress)); -#endif break; case StructProperty::Removed: lua_pushnil(l); @@ -2991,19 +3033,38 @@ void LuaScriptInterface::LuaSetProperty(lua_State* l, StructProperty property, i *((String*)propertyAddress) = tpt_lua_checkString(l, stackPos); break; case StructProperty::Colour: -#if PIXELSIZE == 4 *((unsigned int*)propertyAddress) = int32_truncate(luaL_checknumber(l, stackPos)); -#else - *((unsigned short*)propertyAddress) = int32_truncate(luaL_checknumber(l, stackPos)); -#endif break; case StructProperty::Removed: break; } } + +void LuaScriptInterface::LuaSetParticleProperty(lua_State* l, int particleID, StructProperty property, intptr_t propertyAddress, int stackPos) +{ + if (property.Name == "type") + { + luacon_sim->part_change_type(particleID, int(luacon_sim->parts[particleID].x+0.5f), int(luacon_sim->parts[particleID].y+0.5f), luaL_checkinteger(l, 3)); + } + else if (property.Name == "x" || property.Name == "y") + { + float val = luaL_checknumber(l, 3); + float x = luacon_sim->parts[particleID].x; + float y = luacon_sim->parts[particleID].y; + float nx = property.Name == "x" ? val : x; + float ny = property.Name == "y" ? val : y; + luacon_sim->move(particleID, (int)(x + 0.5f), (int)(y + 0.5f), nx, ny); + } + else + { + LuaSetProperty(l, property, propertyAddress, 3); + } +} + int LuaScriptInterface::elements_loadDefault(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); int args = lua_gettop(l); if (args) { @@ -3067,6 +3128,7 @@ int LuaScriptInterface::elements_loadDefault(lua_State * l) int LuaScriptInterface::elements_allocate(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); luaL_checktype(l, 1, LUA_TSTRING); luaL_checktype(l, 2, LUA_TSTRING); auto group = tpt_lua_toByteString(l, 1).ToUpper(); @@ -3143,6 +3205,7 @@ int LuaScriptInterface::elements_allocate(lua_State * l) static int luaUpdateWrapper(UPDATE_FUNC_ARGS) { + auto *luacon_ci = static_cast(commandInterface); auto *builtinUpdate = GetElements()[parts[i].type].Update; if (builtinUpdate && lua_el_mode[parts[i].type] == 1) { @@ -3160,7 +3223,7 @@ static int luaUpdateWrapper(UPDATE_FUNC_ARGS) lua_pushinteger(luacon_ci->l, y); lua_pushinteger(luacon_ci->l, surround_space); lua_pushinteger(luacon_ci->l, nt); - callret = lua_pcall(luacon_ci->l, 5, 1, 0); + callret = tpt_lua_pcall(luacon_ci->l, 5, 1, 0, true); if (callret) luacon_ci->Log(CommandInterface::LogError, luacon_geterror()); if(lua_isboolean(luacon_ci->l, -1)){ @@ -3186,6 +3249,7 @@ static int luaUpdateWrapper(UPDATE_FUNC_ARGS) static int luaGraphicsWrapper(GRAPHICS_FUNC_ARGS) { + auto *luacon_ci = static_cast(commandInterface); if (lua_gr_func[cpart->type]) { int cache = 0, callret; @@ -3195,7 +3259,7 @@ static int luaGraphicsWrapper(GRAPHICS_FUNC_ARGS) lua_pushinteger(luacon_ci->l, *colr); lua_pushinteger(luacon_ci->l, *colg); lua_pushinteger(luacon_ci->l, *colb); - callret = lua_pcall(luacon_ci->l, 4, 10, 0); + callret = tpt_lua_pcall(luacon_ci->l, 4, 10, 0, false); if (callret) { luacon_ci->Log(CommandInterface::LogError, luacon_geterror()); @@ -3232,6 +3296,7 @@ static int luaGraphicsWrapper(GRAPHICS_FUNC_ARGS) static void luaCreateWrapper(ELEMENT_CREATE_FUNC_ARGS) { + auto *luacon_ci = static_cast(commandInterface); if (luaCreateHandlers[sim->parts[i].type]) { lua_rawgeti(luacon_ci->l, LUA_REGISTRYINDEX, luaCreateHandlers[sim->parts[i].type]); @@ -3240,7 +3305,7 @@ static void luaCreateWrapper(ELEMENT_CREATE_FUNC_ARGS) lua_pushinteger(luacon_ci->l, y); lua_pushinteger(luacon_ci->l, t); lua_pushinteger(luacon_ci->l, v); - if (lua_pcall(luacon_ci->l, 5, 0, 0)) + if (tpt_lua_pcall(luacon_ci->l, 5, 0, 0, true)) { luacon_ci->Log(CommandInterface::LogError, "In create func: " + luacon_geterror()); lua_pop(luacon_ci->l, 1); @@ -3250,6 +3315,7 @@ static void luaCreateWrapper(ELEMENT_CREATE_FUNC_ARGS) static bool luaCreateAllowedWrapper(ELEMENT_CREATE_ALLOWED_FUNC_ARGS) { + auto *luacon_ci = static_cast(commandInterface); bool ret = false; if (luaCreateAllowedHandlers[t]) { @@ -3258,7 +3324,7 @@ static bool luaCreateAllowedWrapper(ELEMENT_CREATE_ALLOWED_FUNC_ARGS) lua_pushinteger(luacon_ci->l, x); lua_pushinteger(luacon_ci->l, y); lua_pushinteger(luacon_ci->l, t); - if (lua_pcall(luacon_ci->l, 4, 1, 0)) + if (tpt_lua_pcall(luacon_ci->l, 4, 1, 0, true)) { luacon_ci->Log(CommandInterface::LogError, "In create allowed: " + luacon_geterror()); lua_pop(luacon_ci->l, 1); @@ -3275,6 +3341,7 @@ static bool luaCreateAllowedWrapper(ELEMENT_CREATE_ALLOWED_FUNC_ARGS) static void luaChangeTypeWrapper(ELEMENT_CHANGETYPE_FUNC_ARGS) { + auto *luacon_ci = static_cast(commandInterface); if (luaChangeTypeHandlers[sim->parts[i].type]) { lua_rawgeti(luacon_ci->l, LUA_REGISTRYINDEX, luaChangeTypeHandlers[sim->parts[i].type]); @@ -3283,7 +3350,7 @@ static void luaChangeTypeWrapper(ELEMENT_CHANGETYPE_FUNC_ARGS) lua_pushinteger(luacon_ci->l, y); lua_pushinteger(luacon_ci->l, from); lua_pushinteger(luacon_ci->l, to); - if (lua_pcall(luacon_ci->l, 5, 0, 0)) + if (tpt_lua_pcall(luacon_ci->l, 5, 0, 0, true)) { luacon_ci->Log(CommandInterface::LogError, "In change type: " + luacon_geterror()); lua_pop(luacon_ci->l, 1); @@ -3293,6 +3360,7 @@ static void luaChangeTypeWrapper(ELEMENT_CHANGETYPE_FUNC_ARGS) static bool luaCtypeDrawWrapper(CTYPEDRAW_FUNC_ARGS) { + auto *luacon_ci = static_cast(commandInterface); bool ret = false; if (luaCtypeDrawHandlers[sim->parts[i].type]) { @@ -3300,7 +3368,7 @@ static bool luaCtypeDrawWrapper(CTYPEDRAW_FUNC_ARGS) lua_pushinteger(luacon_ci->l, i); lua_pushinteger(luacon_ci->l, t); lua_pushinteger(luacon_ci->l, v); - if (lua_pcall(luacon_ci->l, 3, 1, 0)) + if (tpt_lua_pcall(luacon_ci->l, 3, 1, 0, true)) { luacon_ci->Log(CommandInterface::LogError, luacon_geterror()); lua_pop(luacon_ci->l, 1); @@ -3317,6 +3385,7 @@ static bool luaCtypeDrawWrapper(CTYPEDRAW_FUNC_ARGS) int LuaScriptInterface::elements_element(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); int id = luaL_checkinteger(l, 1); if (!luacon_sim->IsElementOrNone(id)) { @@ -3503,6 +3572,7 @@ void LuaScriptInterface::SetDefaultProperties(lua_State * l, int id, int stackPo int LuaScriptInterface::elements_property(lua_State * l) { + auto *luacon_ci = static_cast(commandInterface); int id = luaL_checkinteger(l, 1); if (!luacon_sim->IsElementOrNone(id)) { @@ -3729,12 +3799,11 @@ void LuaScriptInterface::initGraphicsAPI() int LuaScriptInterface::graphics_textSize(lua_State * l) { - int width, height; auto text = tpt_lua_optString(l, 1, ""); - Graphics::textsize(text, width, height); + auto size = Graphics::TextSize(text); - lua_pushinteger(l, width); - lua_pushinteger(l, height); + lua_pushinteger(l, size.X); + lua_pushinteger(l, size.Y); return 2; } @@ -3757,7 +3826,7 @@ int LuaScriptInterface::graphics_drawText(lua_State * l) if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->drawtext(x, y, text, r, g, b, a); + luacon_g->BlendText({ x, y }, text, RGBA(r, g, b, a)); return 0; } @@ -3781,7 +3850,14 @@ int LuaScriptInterface::graphics_drawLine(lua_State * l) if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->draw_line(x1, y1, x2, y2, r, g, b, a); + if (a == 255) + { + luacon_g->DrawLine({ x1, y1 }, { x2, y2 }, RGB(r, g, b)); + } + else + { + luacon_g->BlendLine({ x1, y1 }, { x2, y2 }, RGBA(r, g, b, a)); + } return 0; } @@ -3805,7 +3881,14 @@ int LuaScriptInterface::graphics_drawRect(lua_State * l) if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->drawrect(x, y, width, height, r, g, b, a); + if (a == 255) + { + luacon_g->DrawRect(RectSized(Vec2{ x, y }, Vec2{ width, height }), RGB(r, g, b)); + } + else + { + luacon_g->BlendRect(RectSized(Vec2{ x, y }, Vec2{ width, height }), RGBA(r, g, b, a)); + } return 0; } @@ -3829,7 +3912,14 @@ int LuaScriptInterface::graphics_fillRect(lua_State * l) if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->fillrect(x, y, width, height, r, g, b, a); + if (a == 255) + { + luacon_g->DrawFilledRect(RectSized(Vec2{ x, y }, Vec2{ width, height }), RGB(r, g, b)); + } + else + { + luacon_g->BlendFilledRect(RectSized(Vec2{ x, y }, Vec2{ width, height }), RGBA(r, g, b, a)); + } return 0; } @@ -3853,7 +3943,7 @@ int LuaScriptInterface::graphics_drawCircle(lua_State * l) if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->drawcircle(x, y, abs(rx), abs(ry), r, g, b, a); + luacon_g->BlendEllipse({ x, y }, { abs(rx), abs(ry) }, RGBA(r, g, b, a)); return 0; } @@ -3877,7 +3967,7 @@ int LuaScriptInterface::graphics_fillCircle(lua_State * l) if (a<0) a = 0; else if (a>255) a = 255; - luacon_g->fillcircle(x, y, abs(rx), abs(ry), r, g, b, a); + luacon_g->BlendFilledEllipse({ x, y }, { abs(rx), abs(ry) }, RGBA(r, g, b, a)); return 0; } @@ -3917,14 +4007,23 @@ int LuaScriptInterface::graphics_setClipRect(lua_State * l) int y = luaL_optinteger(l, 2, 0); int w = luaL_optinteger(l, 3, WINDOWW); int h = luaL_optinteger(l, 4, WINDOWH); - luacon_g->SetClipRect(x, y, w, h); - lua_pushinteger(l, x); - lua_pushinteger(l, y); - lua_pushinteger(l, w); - lua_pushinteger(l, h); + auto rect = RectSized(Vec2(x, y), Vec2(w, h)); + luacon_g->SwapClipRect(rect); + lua_pushinteger(l, rect.TopLeft.X); + lua_pushinteger(l, rect.TopLeft.Y); + lua_pushinteger(l, rect.Size().X); + lua_pushinteger(l, rect.Size().Y); return 4; } +static int fsIsLink(lua_State * l) +{ + auto dirname = tpt_lua_checkByteString(l, 1); + bool ret = Platform::IsLink(dirname); + lua_pushboolean(l, ret); + return 1; +} + void LuaScriptInterface::initFileSystemAPI() { //Methods @@ -3933,6 +4032,7 @@ void LuaScriptInterface::initFileSystemAPI() {"exists", fileSystem_exists}, {"isFile", fileSystem_isFile}, {"isDirectory", fileSystem_isDirectory}, + {"isLink", fsIsLink}, {"makeDirectory", fileSystem_makeDirectory}, {"removeDirectory", fileSystem_removeDirectory}, {"removeFile", fileSystem_removeFile}, @@ -4021,7 +4121,8 @@ int LuaScriptInterface::fileSystem_move(lua_State * l) { auto filename = tpt_lua_checkByteString(l, 1); auto newFilename = tpt_lua_checkByteString(l, 2); - lua_pushboolean(l, Platform::RenameFile(filename, newFilename)); + bool replace = lua_toboolean(l, 3); + lua_pushboolean(l, Platform::RenameFile(filename, newFilename, replace)); return 1; } @@ -4040,7 +4141,6 @@ void LuaScriptInterface::initPlatformAPI() struct luaL_Reg platformAPIMethods [] = { {"platform", platform_platform}, {"ident", platform_ident}, - {"build", platform_build}, {"releaseType", platform_releaseType}, {"exeName", platform_exeName}, {"restart", platform_restart}, @@ -4058,25 +4158,19 @@ void LuaScriptInterface::initPlatformAPI() int LuaScriptInterface::platform_platform(lua_State * l) { - lua_pushliteral(l, IDENT_PLATFORM); + tpt_lua_pushByteString(l, IDENT_PLATFORM); return 1; } int LuaScriptInterface::platform_ident(lua_State * l) { - lua_pushliteral(l, IDENT); - return 1; -} - -int LuaScriptInterface::platform_build(lua_State * l) -{ - lua_pushliteral(l, IDENT_BUILD); + tpt_lua_pushByteString(l, IDENT); return 1; } int LuaScriptInterface::platform_releaseType(lua_State * l) { - lua_pushliteral(l, IDENT_RELTYPE); + tpt_lua_pushByteString(l, ByteString(1, IDENT_RELTYPE)); return 1; } @@ -4132,33 +4226,62 @@ void LuaScriptInterface::initEventAPI() lua_getglobal(l, "event"); lua_setglobal(l, "evt"); - lua_pushinteger(l, LuaEvents::keypress); lua_setfield(l, -2, "keypress"); - lua_pushinteger(l, LuaEvents::keyrelease); lua_setfield(l, -2, "keyrelease"); - lua_pushinteger(l, LuaEvents::textinput); lua_setfield(l, -2, "textinput"); - lua_pushinteger(l, LuaEvents::textediting); lua_setfield(l, -2, "textediting"); - lua_pushinteger(l, LuaEvents::mousedown); lua_setfield(l, -2, "mousedown"); - lua_pushinteger(l, LuaEvents::mouseup); lua_setfield(l, -2, "mouseup"); - lua_pushinteger(l, LuaEvents::mousemove); lua_setfield(l, -2, "mousemove"); - lua_pushinteger(l, LuaEvents::mousewheel); lua_setfield(l, -2, "mousewheel"); - lua_pushinteger(l, LuaEvents::tick); lua_setfield(l, -2, "tick"); - lua_pushinteger(l, LuaEvents::blur); lua_setfield(l, -2, "blur"); - lua_pushinteger(l, LuaEvents::close); lua_setfield(l, -2, "close"); - lua_pushinteger(l, LuaEvents::beforesim); lua_setfield(l, -2, "beforesim"); - lua_pushinteger(l, LuaEvents::aftersim); lua_setfield(l, -2, "aftersim"); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "textinput" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "textediting"); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "keypress" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "keyrelease" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "mousedown" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "mouseup" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "mousemove" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "mousewheel" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "tick" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "blur" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "close" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "beforesim" ); + lua_pushinteger(l, VariantIndex()); lua_setfield(l, -2, "aftersim" ); } int LuaScriptInterface::event_register(lua_State * l) { - int eventName = luaL_checkinteger(l, 1); + auto *luacon_ci = static_cast(commandInterface); + int eventType = luaL_checkinteger(l, 1); luaL_checktype(l, 2, LUA_TFUNCTION); - return LuaEvents::RegisterEventHook(l, ByteString::Build("tptevents-", eventName)); + if (eventType < 0 || eventType >= int(luacon_ci->gameControllerEventHandlers.size())) + { + luaL_error(l, "Invalid event type: %i", lua_tointeger(l, 1)); + } + luacon_ci->gameControllerEventHandlers[eventType].Push(l); + auto length = lua_objlen(l, -1); + lua_pushvalue(l, 2); + lua_rawseti(l, -2, length + 1); + lua_pushvalue(l, 2); + return 1; } int LuaScriptInterface::event_unregister(lua_State * l) { - int eventName = luaL_checkinteger(l, 1); + auto *luacon_ci = static_cast(commandInterface); + int eventType = luaL_checkinteger(l, 1); luaL_checktype(l, 2, LUA_TFUNCTION); - return LuaEvents::UnregisterEventHook(l, ByteString::Build("tptevents-", eventName)); + if (eventType < 0 || eventType >= int(luacon_ci->gameControllerEventHandlers.size())) + { + luaL_error(l, "Invalid event type: %i", lua_tointeger(l, 1)); + } + luacon_ci->gameControllerEventHandlers[eventType].Push(l); + auto length = lua_objlen(l, -1); + int skip = 0; + for (auto i = 1U; i <= length; ++i) + { + lua_rawgeti(l, -1, i); + if (!skip && lua_equal(l, -1, 2)) + { + skip = 1; + } + lua_pop(l, 1); + lua_rawgeti(l, -1, i + skip); + lua_rawseti(l, -2, i); + } + return 0; } int LuaScriptInterface::event_getmodifiers(lua_State * l) @@ -4167,316 +4290,164 @@ int LuaScriptInterface::event_getmodifiers(lua_State * l) return 1; } -class RequestHandle +void LuaScriptInterface::initHttpAPI() { -public: - enum RequestType + LuaHttp::Open(l); +} + +static int PushGameControllerEvent(lua_State * l, const GameControllerEvent &event) +{ + if (auto *textInputEvent = std::get_if(&event)) { - normal, - getAuthToken, - }; - -private: - http::Request *request; - bool dead = false; - RequestType type; - - RequestHandle() = default; - - void FinishGetAuthToken(ByteString &data, int &status_out, std::vector &headers) - { - headers.clear(); - std::istringstream ss(data); - Json::Value root; - try - { - ss >> root; - auto status = root["Status"].asString(); - if (status == "OK") - { - status_out = 200; - data = root["Token"].asString(); - } - else - { - status_out = 403; - data = status; - } - } - catch (std::exception &e) - { - std::cerr << "bad auth response: " << e.what() << std::endl; - status_out = 600; - data.clear(); - } - } - -public: - static int Make(lua_State *l, const ByteString &uri, bool isPost, const ByteString &verb, RequestType type, const std::map &post_data, const std::vector &headers) - { - auto authUser = Client::Ref().GetAuthUser(); - if (type == getAuthToken && !authUser.UserID) - { - lua_pushnil(l); - lua_pushliteral(l, "not authenticated"); - return 2; - } - auto *rh = (RequestHandle *)lua_newuserdata(l, sizeof(RequestHandle)); - if (!rh) - { - return 0; - } - new(rh) RequestHandle(); - rh->type = type; - rh->request = new http::Request(uri); - if (verb.size()) - { - rh->request->Verb(verb); - } - for (auto &header : headers) - { - rh->request->AddHeader(header); - } - if (isPost) - { - rh->request->AddPostData(post_data); - } - if (type == getAuthToken) - { - rh->request->AuthHeaders(ByteString::Build(authUser.UserID), authUser.SessionID); - } - rh->request->Start(); - luaL_newmetatable(l, "HTTPRequest"); - lua_setmetatable(l, -2); + tpt_lua_pushString(l, textInputEvent->text); return 1; } - - ~RequestHandle() + else if (auto *textEditingEvent = std::get_if(&event)) { - if (!Dead()) - { - Cancel(); - } + tpt_lua_pushString(l, textEditingEvent->text); + return 1; } - - bool Dead() const + else if (auto *keyPressEvent = std::get_if(&event)) { - return dead; + lua_pushinteger(l, keyPressEvent->key); + lua_pushinteger(l, keyPressEvent->scan); + lua_pushboolean(l, keyPressEvent->repeat); + lua_pushboolean(l, keyPressEvent->shift); + lua_pushboolean(l, keyPressEvent->ctrl); + lua_pushboolean(l, keyPressEvent->alt); + return 6; } - - bool Done() const + else if (auto *keyReleaseEvent = std::get_if(&event)) { - return dead || request->CheckDone(); + lua_pushinteger(l, keyReleaseEvent->key); + lua_pushinteger(l, keyReleaseEvent->scan); + lua_pushboolean(l, keyReleaseEvent->repeat); + lua_pushboolean(l, keyReleaseEvent->shift); + lua_pushboolean(l, keyReleaseEvent->ctrl); + lua_pushboolean(l, keyReleaseEvent->alt); + return 6; } - - void Progress(int *total, int *done) + else if (auto *mouseDownEvent = std::get_if(&event)) { - if (!dead) - { - request->CheckProgress(total, done); - } + lua_pushinteger(l, mouseDownEvent->x); + lua_pushinteger(l, mouseDownEvent->y); + lua_pushinteger(l, mouseDownEvent->button); + return 3; } - - void Cancel() + else if (auto *mouseUpEvent = std::get_if(&event)) { - if (!dead) - { - request->Cancel(); - dead = true; - } + lua_pushinteger(l, mouseUpEvent->x); + lua_pushinteger(l, mouseUpEvent->y); + lua_pushinteger(l, mouseUpEvent->button); + lua_pushinteger(l, mouseUpEvent->reason); + return 4; } - - ByteString Finish(int &status_out, std::vector &headers) + else if (auto *mouseMoveEvent = std::get_if(&event)) { - ByteString data; - if (!dead) - { - if (request->CheckDone()) - { - data = request->Finish(&status_out, &headers); - if (type == getAuthToken && status_out == 200) - { - FinishGetAuthToken(data, status_out, headers); - } - dead = true; - } - } - return data; + lua_pushinteger(l, mouseMoveEvent->x); + lua_pushinteger(l, mouseMoveEvent->y); + lua_pushinteger(l, mouseMoveEvent->dx); + lua_pushinteger(l, mouseMoveEvent->dy); + return 4; } -}; - -static int http_request_gc(lua_State *l) -{ - auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); - rh->~RequestHandle(); - return 0; -} - -static int http_request_status(lua_State *l) -{ - auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); - if (rh->Dead()) + else if (auto *mouseWheelEvent = std::get_if(&event)) { - lua_pushliteral(l, "dead"); - } - else if (rh->Done()) - { - lua_pushliteral(l, "done"); - } - else - { - lua_pushliteral(l, "running"); - } - return 1; -} - -static int http_request_progress(lua_State *l) -{ - auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); - if (!rh->Dead()) - { - int total, done; - rh->Progress(&total, &done); - lua_pushinteger(l, total); - lua_pushinteger(l, done); - return 2; - } - return 0; -} - -static int http_request_cancel(lua_State *l) -{ - auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); - if (!rh->Dead()) - { - rh->Cancel(); - } - return 0; -} - -static int http_request_finish(lua_State *l) -{ - auto *rh = (RequestHandle *)luaL_checkudata(l, 1, "HTTPRequest"); - if (!rh->Dead()) - { - int status_out; - std::vector headers; - ByteString data = rh->Finish(status_out, headers); - lua_pushlstring(l, &data[0], data.size()); - lua_pushinteger(l, status_out); - lua_newtable(l); - for (auto i = 0; i < int(headers.size()); ++i) - { - lua_pushlstring(l, headers[i].data(), headers[i].size()); - lua_rawseti(l, -2, i + 1); - } + lua_pushinteger(l, mouseWheelEvent->x); + lua_pushinteger(l, mouseWheelEvent->y); + lua_pushinteger(l, mouseWheelEvent->d); return 3; } return 0; } -static int http_request(lua_State *l, bool isPost) +bool LuaScriptInterface::HandleEvent(const GameControllerEvent &event) { - ByteString uri = tpt_lua_checkByteString(l, 1); - std::map post_data; - auto headersIndex = 2; - auto verbIndex = 3; - - if (isPost) + bool cont = true; + gameControllerEventHandlers[event.index()].Push(l); + int len = lua_objlen(l, -1); + for (int i = 1; i <= len && cont; i++) { - headersIndex += 1; - verbIndex += 1; - if (lua_istable(l, 2)) + lua_rawgeti(l, -1, i); + int numArgs = PushGameControllerEvent(l, event); + auto simEvent = std::get_if(&event) || + std::get_if(&event); + int callret = tpt_lua_pcall(l, numArgs, 1, 0, simEvent); + if (callret) { - lua_pushnil(l); - while (lua_next(l, 2)) + if (luacon_geterror() == "Error: Script not responding") { - lua_pushvalue(l, -2); - post_data.emplace(tpt_lua_toByteString(l, -1), tpt_lua_toByteString(l, -2)); - lua_pop(l, 2); - } - } - } - - std::vector headers; - if (lua_istable(l, headersIndex)) - { - auto size = lua_objlen(l, headersIndex); - if (size) - { - for (auto i = 0U; i < size; ++i) - { - lua_rawgeti(l, headersIndex, i + 1); - headers.push_back(tpt_lua_toByteString(l, -1)); - lua_pop(l, 1); + for (int j = i; j <= len - 1; j++) + { + lua_rawgeti(l, -2, j + 1); + lua_rawseti(l, -3, j); + } + lua_pushnil(l); + lua_rawseti(l, -3, len); + i--; } + Log(CommandInterface::LogError, luacon_geterror()); + lua_pop(l, 1); } else { - // old dictionary format - lua_pushnil(l); - while (lua_next(l, headersIndex)) - { - lua_pushvalue(l, -2); - headers.push_back(tpt_lua_toByteString(l, -1) + ByteString(": ") + tpt_lua_toByteString(l, -2)); - lua_pop(l, 2); - } + if (!lua_isnoneornil(l, -1)) + cont = lua_toboolean(l, -1); + lua_pop(l, 1); } + len = lua_objlen(l, -1); } - - auto verb = tpt_lua_optByteString(l, verbIndex, ""); - return RequestHandle::Make(l, uri, isPost, verb, RequestHandle::normal, post_data, headers); -} - -static int http_get_auth_token(lua_State *l) -{ - return RequestHandle::Make(l, SCHEME SERVER "/ExternalAuth.api?Action=Get&Audience=" + format::URLEncode(tpt_lua_checkByteString(l, 1)), false, {}, RequestHandle::getAuthToken, {}, {}); -} - -int LuaScriptInterface::http_get(lua_State * l) -{ - return http_request(l, false); -} - -int LuaScriptInterface::http_post(lua_State * l) -{ - return http_request(l, true); -} - -void LuaScriptInterface::initHttpAPI() -{ - luaL_newmetatable(l, "HTTPRequest"); - lua_pushcfunction(l, http_request_gc); - lua_setfield(l, -2, "__gc"); - lua_newtable(l); - struct luaL_Reg httpRequestIndexMethods[] = { - { "status", http_request_status }, - { "progress", http_request_progress }, - { "cancel", http_request_cancel }, - { "finish", http_request_finish }, - { NULL, NULL } - }; - luaL_register(l, NULL, httpRequestIndexMethods); - lua_setfield(l, -2, "__index"); lua_pop(l, 1); - lua_newtable(l); - struct luaL_Reg httpMethods[] = { - { "get", http_get }, - { "post", http_post }, - { "getAuthToken", http_get_auth_token }, - { NULL, NULL } - }; - luaL_register(l, NULL, httpMethods); - lua_setglobal(l, "http"); -} - -bool LuaScriptInterface::HandleEvent(LuaEvents::EventTypes eventType, Event * event) -{ - return LuaEvents::HandleEvent(this, event, ByteString::Build("tptevents-", eventType)); + return cont; } void LuaScriptInterface::OnTick() { + if (scriptDownload && scriptDownload->CheckDone()) + { + + auto ret = scriptDownload->StatusCode(); + ByteString scriptData; + auto handleResponse = [this, &scriptData, &ret]() { + if (!scriptData.size()) + { + new ErrorMessage("Script download", "Server did not return data"); + return; + } + if (ret != 200) + { + new ErrorMessage("Script download", ByteString(http::StatusText(ret)).FromUtf8()); + return; + } + if (Platform::FileExists(scriptDownloadFilename) && scriptDownloadConfirmPrompt && !ConfirmPrompt::Blocking("File already exists, overwrite?", scriptDownloadFilename.FromUtf8(), "Overwrite")) + { + return; + } + if (!Platform::WriteFile(std::vector(scriptData.begin(), scriptData.end()), scriptDownloadFilename)) + { + new ErrorMessage("Script download", "Unable to write to file"); + return; + } + if (scriptDownloadRunScript) + { + if (tpt_lua_dostring(l, ByteString::Build("dofile('", scriptDownloadFilename, "')"))) + { + new ErrorMessage("Script download", luacon_geterror()); + return; + } + } + new InformationMessage("Script download", "Script successfully downloaded", false); + }; + try + { + scriptData = scriptDownload->Finish().second; + handleResponse(); + } + catch (const http::RequestError &ex) + { + new ErrorMessage("Script download", ByteString(ex.what()).FromUtf8()); + } + scriptDownload.reset(); + } lua_getglobal(l, "simulation"); if (lua_istable(l, -1)) { @@ -4484,8 +4455,7 @@ void LuaScriptInterface::OnTick() lua_setfield(l, -2, "NUM_PARTS"); } lua_pop(l, 1); - TickEvent ev; - HandleEvent(LuaEvents::tick, &ev); + HandleEvent(TickEvent{}); } int LuaScriptInterface::Command(String command) @@ -4494,8 +4464,8 @@ int LuaScriptInterface::Command(String command) luacon_hasLastError = false; if (command[0] == '!') { - int ret = legacy->Command(command.Substr(1)); - lastError = legacy->GetLastError(); + int ret = TPTScriptInterface::Command(command.Substr(1)); + lastError = GetLastError(); return ret; } else @@ -4506,7 +4476,6 @@ int LuaScriptInterface::Command(String command) lastCode += "\n"; lastCode += command; ByteString tmp = ("return " + lastCode).ToUtf8(); - ui::Engine::Ref().LastTick(Platform::GetTime()); luaL_loadbuffer(l, tmp.data(), tmp.size(), "@console"); if (lua_type(l, -1) != LUA_TFUNCTION) { @@ -4526,7 +4495,7 @@ int LuaScriptInterface::Command(String command) else { lastCode = ""; - ret = lua_pcall(l, 0, LUA_MULTRET, 0); + ret = tpt_lua_pcall(l, 0, LUA_MULTRET, 0, false); if (ret) { lastError = luacon_geterror(); @@ -4763,17 +4732,18 @@ String LuaScriptInterface::FormatCommand(String command) { if(command.size() && command[0] == '!') { - return "!"+legacy->FormatCommand(command.Substr(1)); + return "!" + TPTScriptInterface::FormatCommand(command.Substr(1)); } else return highlight(command); } -LuaScriptInterface::~LuaScriptInterface() { +LuaScriptInterface::~LuaScriptInterface() +{ delete tptPart; for (auto &component_and_ref : grabbed_components) { - luacon_ci->Window->RemoveComponent(component_and_ref.first->GetComponent()); + Window->RemoveComponent(component_and_ref.first->GetComponent()); component_and_ref.second.Clear(); component_and_ref.first->owner_ref = component_and_ref.second; component_and_ref.first->SetParentWindow(nullptr); @@ -4782,20 +4752,18 @@ LuaScriptInterface::~LuaScriptInterface() { luaCreateAllowedHandlers.clear(); luaCreateHandlers.clear(); luaCtypeDrawHandlers.clear(); + gameControllerEventHandlers.clear(); lua_el_mode_v.clear(); lua_el_func_v.clear(); lua_gr_func_v.clear(); lua_cd_func_v.clear(); lua_close(l); - delete legacy; } -#ifndef NOHTTP void LuaScriptInterface::initSocketAPI() { - LuaTCPSocket::Open(l); + LuaSocket::Open(l); } -#endif void tpt_lua_pushByteString(lua_State *L, const ByteString &str) { @@ -4862,7 +4830,7 @@ int tpt_lua_loadstring(lua_State *L, const ByteString &str) int tpt_lua_dostring(lua_State *L, const ByteString &str) { - return tpt_lua_loadstring(L, str) || lua_pcall(L, 0, LUA_MULTRET, 0); + return tpt_lua_loadstring(L, str) || tpt_lua_pcall(L, 0, LUA_MULTRET, 0, false); } bool tpt_lua_equalsString(lua_State *L, int index, const char *data, size_t size) @@ -4870,4 +4838,60 @@ bool tpt_lua_equalsString(lua_State *L, int index, const char *data, size_t size return lua_isstring(L, index) && lua_objlen(L, index) == size && !memcmp(lua_tostring(L, index), data, size); } -#endif +int tpt_lua_pcall(lua_State *L, int numArgs, int numResults, int errorFunc, bool simEvent) +{ + auto *luacon_ci = static_cast(commandInterface); + luacon_ci->luaExecutionStart = Platform::GetTime(); + struct AtReturn + { + bool oldInSimEvent; + + AtReturn(bool newInSimEvent) + { + oldInSimEvent = inSimEvent; + inSimEvent = newInSimEvent; + } + + ~AtReturn() + { + inSimEvent = oldInSimEvent; + } + } atReturn(simEvent); + return lua_pcall(L, numArgs, numResults, errorFunc); +} + +CommandInterface *CommandInterface::Create(GameController * c, GameModel * m) +{ + return new LuaScriptInterface(c, m); +} + +int LuaScriptInterface::luatpt_getscript(lua_State* l) +{ + auto *luacon_ci = static_cast(commandInterface); + + int scriptID = luaL_checkinteger(l, 1); + auto filename = tpt_lua_checkByteString(l, 2); + bool runScript = luaL_optint(l, 3, 0); + int confirmPrompt = luaL_optint(l, 4, 1); + + if (luacon_ci->scriptDownload) + { + new ErrorMessage("Script download", "A script download is already pending"); + return 0; + } + + ByteString url = ByteString::Build(SCHEME, "starcatcher.us/scripts/main.lua?get=", scriptID); + if (confirmPrompt && !ConfirmPrompt::Blocking("Do you want to install script?", url.FromUtf8(), "Install")) + { + return 0; + } + + luacon_ci->scriptDownload = std::make_unique(url); + luacon_ci->scriptDownload->Start(); + luacon_ci->scriptDownloadFilename = filename; + luacon_ci->scriptDownloadRunScript = runScript; + luacon_ci->scriptDownloadConfirmPrompt = confirmPrompt; + + luacon_controller->HideConsole(); + return 0; +} diff --git a/src/lua/LuaScriptInterface.h b/src/lua/LuaScriptInterface.h index d009c40ce..dcc1fe5e6 100644 --- a/src/lua/LuaScriptInterface.h +++ b/src/lua/LuaScriptInterface.h @@ -1,16 +1,18 @@ -#ifndef LUASCRIPTINTERFACE_H_ -#define LUASCRIPTINTERFACE_H_ -#include "Config.h" - +#pragma once #include "LuaCompat.h" #include "LuaSmartRef.h" - #include "CommandInterface.h" -#include "lua/LuaEvents.h" +#include "gui/game/GameControllerEvents.h" +#include "TPTScriptInterface.h" #include "simulation/StructProperty.h" #include "simulation/ElementDefs.h" - #include +#include + +namespace http +{ + class Request; +} namespace ui { @@ -21,30 +23,20 @@ class Tool; //Because lua only has bindings for C, we're going to have to go outside "outside" the LuaScriptInterface, this means we can only have one instance :( -#define LUACON_MDOWN 1 -#define LUACON_MUP 2 -#define LUACON_MPRESS 3 -#define LUACON_MUPALT 4 -#define LUACON_MUPZOOM 5 -#define LUACON_KDOWN 1 -#define LUACON_KUP 2 - -//Bitmasks for things that might need recalculating after changes to tpt.el -#define LUACON_EL_MODIFIED_CANMOVE 0x1 -#define LUACON_EL_MODIFIED_GRAPHICS 0x2 -#define LUACON_EL_MODIFIED_MENUS 0x4 - class Simulation; -class TPTScriptInterface; class LuaComponent; -class LuaScriptInterface: public CommandInterface +class LuaScriptInterface: public TPTScriptInterface { + std::unique_ptr scriptDownload; + ByteString scriptDownloadFilename; + bool scriptDownloadRunScript; + bool scriptDownloadConfirmPrompt; + int luacon_mousex, luacon_mousey, luacon_mousebutton; ByteString luacon_selectedl, luacon_selectedr, luacon_selectedalt, luacon_selectedreplace; bool luacon_mousedown; bool currentCommand; - TPTScriptInterface * legacy; int textInputRefcount; // signs @@ -123,6 +115,7 @@ class LuaScriptInterface: public CommandInterface static int simulation_removeCustomGol(lua_State *l); static int simulation_lastUpdatedID(lua_State *l); static int simulation_updateUpTo(lua_State *l); + static int simulation_temperatureScale(lua_State *l); //Renderer @@ -187,7 +180,6 @@ class LuaScriptInterface: public CommandInterface void initPlatformAPI(); static int platform_platform(lua_State * l); static int platform_ident(lua_State * l); - static int platform_build(lua_State * l); static int platform_releaseType(lua_State * l); static int platform_exeName(lua_State * l); static int platform_restart(lua_State * l); @@ -200,14 +192,14 @@ class LuaScriptInterface: public CommandInterface static int event_unregister(lua_State * l); static int event_getmodifiers(lua_State * l); - void initHttpAPI(); - static int http_get(lua_State * l); - static int http_post(lua_State * l); + static int luatpt_getscript(lua_State * l); + void initHttpAPI(); void initSocketAPI(); std::vector lua_el_func_v, lua_gr_func_v, lua_cd_func_v; std::vector lua_el_mode_v; + std::vector gameControllerEventHandlers; public: int tpt_index(lua_State *l); @@ -215,9 +207,11 @@ public: static void LuaGetProperty(lua_State* l, StructProperty property, intptr_t propertyAddress); static void LuaSetProperty(lua_State* l, StructProperty property, intptr_t propertyAddress, int stackPos); + static void LuaSetParticleProperty(lua_State* l, int particleID, StructProperty property, intptr_t propertyAddress, int stackPos); ui::Window * Window; lua_State *l; + long unsigned int luaExecutionStart = 0; std::map grabbed_components; LuaScriptInterface(GameController * c, GameModel * m); @@ -225,17 +219,15 @@ public: void custom_init_can_move(); void OnTick() override; - bool HandleEvent(LuaEvents::EventTypes eventType, Event * event) override; + bool HandleEvent(const GameControllerEvent &event) override; - void Init(); + void Init() override; void SetWindow(ui::Window * window); int Command(String command) override; String FormatCommand(String command) override; virtual ~LuaScriptInterface(); }; -extern LuaScriptInterface *luacon_ci; - void tpt_lua_pushByteString(lua_State *L, const ByteString &str); void tpt_lua_pushString(lua_State *L, const String &str); @@ -262,4 +254,5 @@ bool tpt_lua_equalsLiteral(lua_State *L, int index, const char (&lit)[N]) return tpt_lua_equalsString(L, index, lit, N - 1U); } -#endif /* LUASCRIPTINTERFACE_H_ */ +int tpt_lua_pcall(lua_State *L, int numArgs, int numResults, int errorFunc, bool simEvent); + diff --git a/src/lua/LuaSlider.cpp b/src/lua/LuaSlider.cpp index e79eb693f..0daa5c9e2 100644 --- a/src/lua/LuaSlider.cpp +++ b/src/lua/LuaSlider.cpp @@ -1,10 +1,5 @@ -#include "Config.h" -#ifdef LUACONSOLE - #include "LuaSlider.h" - #include "LuaScriptInterface.h" - #include "gui/interface/Slider.h" const char LuaSlider::className[] = "Slider"; @@ -77,7 +72,7 @@ void LuaSlider::triggerOnValueChanged() lua_rawgeti(l, LUA_REGISTRYINDEX, onValueChangedFunction); lua_rawgeti(l, LUA_REGISTRYINDEX, owner_ref); lua_pushinteger(l, slider->GetValue()); - if (lua_pcall(l, 2, 0, 0)) + if (tpt_lua_pcall(l, 2, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -87,4 +82,3 @@ void LuaSlider::triggerOnValueChanged() LuaSlider::~LuaSlider() { } -#endif diff --git a/src/lua/LuaSmartRef.cpp b/src/lua/LuaSmartRef.cpp index 4503a7f77..4fff77dd1 100644 --- a/src/lua/LuaSmartRef.cpp +++ b/src/lua/LuaSmartRef.cpp @@ -1,5 +1,3 @@ -#include "Config.h" -#ifdef LUACONSOLE #include "LuaSmartRef.h" void LuaSmartRef::Clear() @@ -37,5 +35,3 @@ int LuaSmartRef::Push(lua_State *l) lua_rawgeti(l, LUA_REGISTRYINDEX, ref); return lua_type(l, -1); } - -#endif diff --git a/src/lua/LuaSmartRef.h b/src/lua/LuaSmartRef.h index 60ee9270c..69a69bc38 100644 --- a/src/lua/LuaSmartRef.h +++ b/src/lua/LuaSmartRef.h @@ -1,5 +1,4 @@ #pragma once - #include "LuaCompat.h" class LuaSmartRef diff --git a/src/lua/LuaSocket.cpp b/src/lua/LuaSocket.cpp new file mode 100644 index 000000000..9083968b3 --- /dev/null +++ b/src/lua/LuaSocket.cpp @@ -0,0 +1,33 @@ +#include "LuaSocket.h" +#include "LuaScriptInterface.h" +#include "Misc.h" +#include +#include + +namespace LuaSocket +{ + static int GetTime(lua_State *l) + { + lua_pushnumber(l, Now()); + return 1; + } + + static int Sleep(lua_State *l) + { + Timeout(luaL_checknumber(l, 1)); + return 0; + } + + void Open(lua_State *l) + { + lua_newtable(l); + struct luaL_Reg socketMethods[] = { + { "sleep", LuaSocket::Sleep }, + { "gettime", LuaSocket::GetTime }, + { NULL, NULL }, + }; + luaL_register(l, NULL, socketMethods); + lua_setglobal(l, "socket"); + OpenTCP(l); + } +} diff --git a/src/lua/LuaSocket.h b/src/lua/LuaSocket.h new file mode 100644 index 000000000..b0be94116 --- /dev/null +++ b/src/lua/LuaSocket.h @@ -0,0 +1,10 @@ +#pragma once +#include "LuaCompat.h" + +namespace LuaSocket +{ + double Now(); + void Timeout(double timeout); + void Open(lua_State *l); + void OpenTCP(lua_State *l); +} diff --git a/src/lua/LuaSocketDefault.cpp b/src/lua/LuaSocketDefault.cpp new file mode 100644 index 000000000..6c4723425 --- /dev/null +++ b/src/lua/LuaSocketDefault.cpp @@ -0,0 +1,32 @@ +#include "LuaSocket.h" +#include "LuaScriptInterface.h" +#include "Misc.h" +#include +#include +#include +#include + +namespace LuaSocket +{ + double Now() + { + struct timeval rt; + gettimeofday(&rt, (struct timezone *)NULL); + return rt.tv_sec + rt.tv_usec / 1e6; + } + + void Timeout(double timeout) + { + struct timespec req, rem; + if (timeout < 0.0) timeout = 0.0; + if (timeout > INT_MAX) timeout = INT_MAX; + req.tv_sec = int(timeout); + req.tv_nsec = int((timeout - req.tv_sec) * 1000000000); + if (req.tv_nsec > 999999999) req.tv_nsec = 999999999; + while (nanosleep(&req, &rem)) + { + req.tv_sec = rem.tv_sec; + req.tv_nsec = rem.tv_nsec; + } + } +} diff --git a/src/lua/LuaTCPSocket.cpp b/src/lua/LuaSocketTCPHttp.cpp similarity index 71% rename from src/lua/LuaTCPSocket.cpp rename to src/lua/LuaSocketTCPHttp.cpp index b18541182..3c58c0de1 100644 --- a/src/lua/LuaTCPSocket.cpp +++ b/src/lua/LuaSocketTCPHttp.cpp @@ -1,77 +1,20 @@ -#include "LuaTCPSocket.h" +#include "LuaSocket.h" -#ifndef NOHTTP -# include "common/String.h" -# include -# include -# include -# include -#endif +#include "common/String.h" +#include +#include +#include +#include #include #include -#ifdef WIN -# include -# include -#else -# include -# include -#endif #include "LuaScriptInterface.h" +#include "client/http/requestmanager/RequestManager.h" +#include "client/http/requestmanager/CurlError.h" #include "Misc.h" -void SetupCurlEasyCiphers(CURL *easy); - -namespace LuaTCPSocket +namespace LuaSocket { - static double Now() - { -#ifdef WIN - FILETIME rt; - GetSystemTimeAsFileTime(&rt); - return (rt.dwLowDateTime + (uint64_t(rt.dwHighDateTime) << 32) - uint64_t(116444736000000000ULL)) / 1e7; -#else - struct timeval rt; - gettimeofday(&rt, (struct timezone *)NULL); - return rt.tv_sec + rt.tv_usec / 1e6; -#endif - } - - static int GetTime(lua_State *l) - { - lua_pushnumber(l, Now()); - return 1; - } - - static void Timeout(double timeout) - { -#ifdef WIN - if (timeout < 0.0) timeout = 0.0; - if (timeout < DBL_MAX / 1000.0) timeout *= 1000.0; - if (timeout > INT_MAX) timeout = INT_MAX; - ::Sleep(int(timeout)); -#else - struct timespec req, rem; - if (timeout < 0.0) timeout = 0.0; - if (timeout > INT_MAX) timeout = INT_MAX; - req.tv_sec = int(timeout); - req.tv_nsec = int((timeout - req.tv_sec) * 1000000000); - if (req.tv_nsec > 999999999) req.tv_nsec = 999999999; - while (nanosleep(&req, &rem)) - { - req.tv_sec = rem.tv_sec; - req.tv_nsec = rem.tv_nsec; - } -#endif - } - - static int Sleep(lua_State *l) - { - Timeout(luaL_checknumber(l, 1)); - return 0; - } - -#ifndef NOHTTP enum Status { StatusReady, @@ -97,9 +40,10 @@ namespace LuaTCPSocket static void Reset(TCPSocket *tcps) { + using http::HandleCURLMcode; if (tcps->multi) { - curl_multi_remove_handle(tcps->multi, tcps->easy); + HandleCURLMcode(curl_multi_remove_handle(tcps->multi, tcps->easy)); curl_multi_cleanup(tcps->multi); tcps->multi = nullptr; } @@ -113,10 +57,12 @@ namespace LuaTCPSocket static bool ConnectPerform(TCPSocket *tcps, CURLcode *res) { + using http::HandleCURLMcode; while (true) { int dontcare; auto mres = curl_multi_perform(tcps->multi, &dontcare); + http::HandleCURLMcode(mres); struct CURLMsg *msg; while ((msg = curl_multi_info_read(tcps->multi, &dontcare))) { @@ -136,8 +82,13 @@ namespace LuaTCPSocket static int New(lua_State *l) { + using http::HandleCURLMcode; + if (http::RequestManager::Ref().DisableNetwork()) + { + return luaL_error(l, "network disabled"); + } auto *tcps = (TCPSocket *)lua_newuserdata(l, sizeof(TCPSocket)); - new (tcps) TCPSocket; + new(tcps) TCPSocket; tcps->errorBuf[0] = 0; tcps->easy = curl_easy_init(); tcps->status = StatusReady; @@ -157,7 +108,7 @@ namespace LuaTCPSocket Reset(tcps); return luaL_error(l, "curl_multi_init failed"); } - curl_multi_add_handle(tcps->multi, tcps->easy); + HandleCURLMcode(curl_multi_add_handle(tcps->multi, tcps->easy)); luaL_newmetatable(l, "TCPSocket"); lua_setmetatable(l, -2); return 1; @@ -440,6 +391,7 @@ namespace LuaTCPSocket static int Connect(lua_State *l) { + using http::HandleCURLcode; auto *tcps = (TCPSocket *)luaL_checkudata(l, 1, "TCPSocket"); if (tcps->status == StatusDead) { @@ -454,43 +406,51 @@ namespace LuaTCPSocket { if (tcps->status != StatusConnecting) { - tcps->status = StatusConnecting; - // * Using CURLPROTO_HTTPS and CURLPROTO_HTTP with CURL_HTTP_VERSION_1_0 - // because these really don't send anything while connecting if - // CURLOPT_CONNECT_ONLY is 1 and there are no proxies involved. The - // only ugly bit is that we have to prepend http:// or https:// to - // the hostnames. - curl_easy_setopt(tcps->easy, CURLOPT_ERRORBUFFER, tcps->errorBuf); - curl_easy_setopt(tcps->easy, CURLOPT_CONNECT_ONLY, 1L); - ByteString address = tpt_lua_checkByteString(l, 2); - curl_easy_setopt(tcps->easy, CURLOPT_PORT, long(luaL_checkinteger(l, 3))); - curl_easy_setopt(tcps->easy, CURLOPT_NOSIGNAL, 1L); - curl_easy_setopt(tcps->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); - if (lua_toboolean(l, 4)) + try { + tcps->status = StatusConnecting; + // * Using CURLPROTO_HTTPS and CURLPROTO_HTTP with CURL_HTTP_VERSION_1_0 + // because these really don't send anything while connecting if + // CURLOPT_CONNECT_ONLY is 1 and there are no proxies involved. The + // only ugly bit is that we have to prepend http:// or https:// to + // the hostnames. + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_ERRORBUFFER, tcps->errorBuf)); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_CONNECT_ONLY, 1L)); + ByteString address = tpt_lua_checkByteString(l, 2); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PORT, long(luaL_checkinteger(l, 3)))); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_NOSIGNAL, 1L)); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0)); + if (lua_toboolean(l, 4)) + { #if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0) - curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS_STR, "https"); - curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS_STR, "https"); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS_STR, "https")); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS_STR, "https")); #else - curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS); - curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTPS)); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTPS)); #endif - SetupCurlEasyCiphers(tcps->easy); - address = "https://" + address; + http::SetupCurlEasyCiphers(tcps->easy); + address = "https://" + address; + } + else + { +#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0) + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS_STR, "http")); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS_STR, "http")); +#else + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)); + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP)); +#endif + address = "http://" + address; + } + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_URL, address.c_str())); } - else + catch (const http::CurlError &ex) { -#if defined(CURL_AT_LEAST_VERSION) && CURL_AT_LEAST_VERSION(7, 85, 0) - curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS_STR, "http"); - curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS_STR, "http"); -#else - curl_easy_setopt(tcps->easy, CURLOPT_PROTOCOLS, CURLPROTO_HTTP); - curl_easy_setopt(tcps->easy, CURLOPT_REDIR_PROTOCOLS, CURLPROTO_HTTP); -#endif - address = "http://" + address; + return luaL_error(l, ex.what()); } - curl_easy_setopt(tcps->easy, CURLOPT_URL, address.c_str()); } + CURLcode res; if (!ConnectPerform(tcps, &res)) { @@ -542,16 +502,17 @@ namespace LuaTCPSocket static int GetPeerName(lua_State *l) { + using http::HandleCURLcode; auto *tcps = (TCPSocket *)luaL_checkudata(l, 1, "TCPSocket"); if (tcps->status != StatusConnected) { return luaL_error(l, "attempt to get remote socket info while not connected"); } char *address; - curl_easy_getinfo(tcps->easy, CURLINFO_PRIMARY_IP, &address); + HandleCURLcode(curl_easy_getinfo(tcps->easy, CURLINFO_PRIMARY_IP, &address)); lua_pushstring(l, address); long port; - curl_easy_getinfo(tcps->easy, CURLINFO_PRIMARY_PORT, &port); + HandleCURLcode(curl_easy_getinfo(tcps->easy, CURLINFO_PRIMARY_PORT, &port)); lua_pushinteger(l, port); return 2; } @@ -578,38 +539,47 @@ namespace LuaTCPSocket static int GetSockName(lua_State *l) { + using http::HandleCURLcode; auto *tcps = (TCPSocket *)luaL_checkudata(l, 1, "TCPSocket"); if (tcps->status != StatusConnected) { return luaL_error(l, "attempt to get local socket info while not connected"); } char *address; - curl_easy_getinfo(tcps->easy, CURLINFO_LOCAL_IP, &address); + HandleCURLcode(curl_easy_getinfo(tcps->easy, CURLINFO_LOCAL_IP, &address)); lua_pushstring(l, address); long port; - curl_easy_getinfo(tcps->easy, CURLINFO_LOCAL_PORT, &port); + HandleCURLcode(curl_easy_getinfo(tcps->easy, CURLINFO_LOCAL_PORT, &port)); lua_pushinteger(l, port); return 2; } static int SetOption(lua_State *l) { + using http::HandleCURLcode; auto *tcps = (TCPSocket *)luaL_checkudata(l, 1, "TCPSocket"); auto option = tpt_lua_checkByteString(l, 2); - if (byteStringEqualsLiteral(option, "keepalive")) + try { - curl_easy_setopt(tcps->easy, CURLOPT_TCP_KEEPALIVE, long(lua_toboolean(l, 3))); - return 0; + if (byteStringEqualsLiteral(option, "keepalive")) + { + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_TCP_KEEPALIVE, long(lua_toboolean(l, 3)))); + return 0; + } + else if (byteStringEqualsLiteral(option, "tcp-nodelay")) + { + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_TCP_NODELAY, long(lua_toboolean(l, 3)))); + return 0; + } + else if (byteStringEqualsLiteral(option, "verify-peer")) + { + HandleCURLcode(curl_easy_setopt(tcps->easy, CURLOPT_SSL_VERIFYPEER, long(lua_toboolean(l, 3)))); + return 0; + } } - else if (byteStringEqualsLiteral(option, "tcp-nodelay")) + catch (const http::CurlError &ex) { - curl_easy_setopt(tcps->easy, CURLOPT_TCP_NODELAY, long(lua_toboolean(l, 3))); - return 0; - } - else if (byteStringEqualsLiteral(option, "verify-peer")) - { - curl_easy_setopt(tcps->easy, CURLOPT_SSL_VERIFYPEER, long(lua_toboolean(l, 3))); - return 0; + return luaL_error(l, ex.what()); } return luaL_error(l, "unknown option"); } @@ -636,43 +606,33 @@ namespace LuaTCPSocket } return luaL_error(l, "unknown direction"); } -#endif - void Open(lua_State *l) + void OpenTCP(lua_State *l) { -#ifndef NOHTTP luaL_newmetatable(l, "TCPSocket"); - lua_pushcfunction(l, LuaTCPSocket::GC); + lua_pushcfunction(l, LuaSocket::GC); lua_setfield(l, -2, "__gc"); lua_newtable(l); struct luaL_Reg tcpSocketIndexMethods[] = { - { "connect", LuaTCPSocket::Connect }, - { "close", LuaTCPSocket::Close }, - { "send", LuaTCPSocket::Send }, - { "receive", LuaTCPSocket::Receive }, - { "lasterror", LuaTCPSocket::LastError }, - { "status", LuaTCPSocket::GetStatus }, - { "getpeername", LuaTCPSocket::GetPeerName }, - { "getsockname", LuaTCPSocket::GetSockName }, - { "settimeout", LuaTCPSocket::SetTimeout }, - { "setoption", LuaTCPSocket::SetOption }, - { "shutdown", LuaTCPSocket::Shutdown }, + { "connect", LuaSocket::Connect }, + { "close", LuaSocket::Close }, + { "send", LuaSocket::Send }, + { "receive", LuaSocket::Receive }, + { "lasterror", LuaSocket::LastError }, + { "status", LuaSocket::GetStatus }, + { "getpeername", LuaSocket::GetPeerName }, + { "getsockname", LuaSocket::GetSockName }, + { "settimeout", LuaSocket::SetTimeout }, + { "setoption", LuaSocket::SetOption }, + { "shutdown", LuaSocket::Shutdown }, { NULL, NULL }, }; luaL_register(l, NULL, tcpSocketIndexMethods); lua_setfield(l, -2, "__index"); lua_pop(l, 1); -#endif - lua_newtable(l); - struct luaL_Reg socketMethods[] = { -#ifndef NOHTTP - { "tcp", LuaTCPSocket::New }, -#endif - { "sleep", LuaTCPSocket::Sleep }, - { "gettime", LuaTCPSocket::GetTime }, - { NULL, NULL }, - }; - luaL_register(l, NULL, socketMethods); - lua_setglobal(l, "socket"); + lua_getglobal(l, "socket"); + lua_pushcfunction(l, LuaSocket::New); + lua_setfield(l, -2, "tcp"); + lua_pop(l, 1); } } diff --git a/src/lua/LuaSocketTCPNoHttp.cpp b/src/lua/LuaSocketTCPNoHttp.cpp new file mode 100644 index 000000000..77b242c05 --- /dev/null +++ b/src/lua/LuaSocketTCPNoHttp.cpp @@ -0,0 +1,8 @@ +#include "LuaSocket.h" + +namespace LuaSocket +{ + void OpenTCP(lua_State *l) + { + } +} diff --git a/src/lua/LuaSocketWindows.cpp b/src/lua/LuaSocketWindows.cpp new file mode 100644 index 000000000..0679febd5 --- /dev/null +++ b/src/lua/LuaSocketWindows.cpp @@ -0,0 +1,25 @@ +#include "LuaSocket.h" +#include "LuaScriptInterface.h" +#include "Misc.h" +#include +#include +#include +#include + +namespace LuaSocket +{ + double Now() + { + FILETIME rt; + GetSystemTimeAsFileTime(&rt); + return (rt.dwLowDateTime + (uint64_t(rt.dwHighDateTime) << 32) - uint64_t(116444736000000000ULL)) / 1e7; + } + + void Timeout(double timeout) + { + if (timeout < 0.0) timeout = 0.0; + if (timeout < DBL_MAX / 1000.0) timeout *= 1000.0; + if (timeout > INT_MAX) timeout = INT_MAX; + ::Sleep(int(timeout)); + } +} diff --git a/src/lua/LuaTextbox.cpp b/src/lua/LuaTextbox.cpp index 16adcd466..64bf2d0de 100644 --- a/src/lua/LuaTextbox.cpp +++ b/src/lua/LuaTextbox.cpp @@ -1,10 +1,5 @@ -#include "Config.h" -#ifdef LUACONSOLE - #include "LuaTextbox.h" - #include "LuaScriptInterface.h" - #include "gui/interface/Textbox.h" const char LuaTextbox::className[] = "Textbox"; @@ -65,7 +60,7 @@ void LuaTextbox::triggerOnTextChanged() { lua_rawgeti(l, LUA_REGISTRYINDEX, onTextChangedFunction); lua_rawgeti(l, LUA_REGISTRYINDEX, owner_ref); - if (lua_pcall(l, 1, 0, 0)) + if (tpt_lua_pcall(l, 1, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_optString(l, -1)); } @@ -90,4 +85,3 @@ int LuaTextbox::text(lua_State * l) LuaTextbox::~LuaTextbox() { } -#endif diff --git a/src/lua/LuaWindow.cpp b/src/lua/LuaWindow.cpp index d88cd76ef..5608048d4 100644 --- a/src/lua/LuaWindow.cpp +++ b/src/lua/LuaWindow.cpp @@ -1,8 +1,4 @@ -#include "Config.h" -#ifdef LUACONSOLE - #include "LuaWindow.h" - #include "LuaScriptInterface.h" #include "LuaButton.h" #include "LuaLabel.h" @@ -10,10 +6,8 @@ #include "LuaCheckbox.h" #include "LuaSlider.h" #include "LuaProgressBar.h" - #include "gui/interface/Window.h" #include "gui/interface/Engine.h" - #include "graphics/Graphics.h" const char LuaWindow::className[] = "Window"; @@ -86,8 +80,8 @@ LuaWindow::LuaWindow(lua_State * l) : void OnDraw() override { Graphics * g = ui::Engine::Ref().g; - g->clearrect(Position.X-2, Position.Y-2, Size.X+4, Size.Y+4); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); luaWindow->triggerOnDraw(); } void OnInitialized() override { luaWindow->triggerOnInitialized(); } @@ -230,7 +224,7 @@ void LuaWindow::triggerOnInitialized() if(onInitializedFunction) { lua_rawgeti(l, LUA_REGISTRYINDEX, onInitializedFunction); - if(lua_pcall(l, 0, 0, 0)) + if(tpt_lua_pcall(l, 0, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -242,7 +236,7 @@ void LuaWindow::triggerOnExit() if(onExitFunction) { lua_rawgeti(l, LUA_REGISTRYINDEX, onExitFunction); - if(lua_pcall(l, 0, 0, 0)) + if(tpt_lua_pcall(l, 0, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -255,7 +249,7 @@ void LuaWindow::triggerOnTick(float dt) { lua_rawgeti(l, LUA_REGISTRYINDEX, onTickFunction); lua_pushnumber(l, dt); - if(lua_pcall(l, 1, 0, 0)) + if(tpt_lua_pcall(l, 1, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -267,7 +261,7 @@ void LuaWindow::triggerOnDraw() if(onDrawFunction) { lua_rawgeti(l, LUA_REGISTRYINDEX, onDrawFunction); - if(lua_pcall(l, 0, 0, 0)) + if(tpt_lua_pcall(l, 0, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -279,7 +273,7 @@ void LuaWindow::triggerOnFocus() if(onFocusFunction) { lua_rawgeti(l, LUA_REGISTRYINDEX, onFocusFunction); - if(lua_pcall(l, 0, 0, 0)) + if(tpt_lua_pcall(l, 0, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -291,7 +285,7 @@ void LuaWindow::triggerOnBlur() if(onBlurFunction) { lua_rawgeti(l, LUA_REGISTRYINDEX, onBlurFunction); - if(lua_pcall(l, 0, 0, 0)) + if(tpt_lua_pcall(l, 0, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -303,7 +297,7 @@ void LuaWindow::triggerOnTryExit() if(onTryExitFunction) { lua_rawgeti(l, LUA_REGISTRYINDEX, onTryExitFunction); - if(lua_pcall(l, 0, 0, 0)) + if(tpt_lua_pcall(l, 0, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -315,7 +309,7 @@ void LuaWindow::triggerOnTryOkay() if(onTryOkayFunction) { lua_rawgeti(l, LUA_REGISTRYINDEX, onTryOkayFunction); - if(lua_pcall(l, 0, 0, 0)) + if(tpt_lua_pcall(l, 0, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -331,7 +325,7 @@ void LuaWindow::triggerOnMouseMove(int x, int y, int dx, int dy) lua_pushinteger(l, y); lua_pushinteger(l, dx); lua_pushinteger(l, dy); - if(lua_pcall(l, 4, 0, 0)) + if(tpt_lua_pcall(l, 4, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -346,7 +340,7 @@ void LuaWindow::triggerOnMouseDown(int x, int y, unsigned button) lua_pushinteger(l, x); lua_pushinteger(l, y); lua_pushinteger(l, button); - if(lua_pcall(l, 3, 0, 0)) + if(tpt_lua_pcall(l, 3, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -361,7 +355,7 @@ void LuaWindow::triggerOnMouseUp(int x, int y, unsigned button) lua_pushinteger(l, x); lua_pushinteger(l, y); lua_pushinteger(l, button); - if(lua_pcall(l, 3, 0, 0)) + if(tpt_lua_pcall(l, 3, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -376,7 +370,7 @@ void LuaWindow::triggerOnMouseWheel(int x, int y, int d) lua_pushinteger(l, x); lua_pushinteger(l, y); lua_pushinteger(l, d); - if(lua_pcall(l, 3, 0, 0)) + if(tpt_lua_pcall(l, 3, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -393,7 +387,7 @@ void LuaWindow::triggerOnKeyPress(int key, int scan, bool repeat, bool shift, bo lua_pushboolean(l, shift); lua_pushboolean(l, ctrl); lua_pushboolean(l, alt); - if(lua_pcall(l, 5, 0, 0)) + if(tpt_lua_pcall(l, 5, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -410,7 +404,7 @@ void LuaWindow::triggerOnKeyRelease(int key, int scan, bool repeat, bool shift, lua_pushboolean(l, shift); lua_pushboolean(l, ctrl); lua_pushboolean(l, alt); - if(lua_pcall(l, 5, 0, 0)) + if(tpt_lua_pcall(l, 5, 0, 0, false)) { ci->Log(CommandInterface::LogError, tpt_lua_toString(l, -1)); } @@ -511,4 +505,3 @@ LuaWindow::~LuaWindow() window->CloseActiveWindow(); delete window; } -#endif diff --git a/src/lua/LuaWindow.h b/src/lua/LuaWindow.h index a82d95328..fe847e101 100644 --- a/src/lua/LuaWindow.h +++ b/src/lua/LuaWindow.h @@ -1,9 +1,7 @@ #pragma once - #include "LuaLuna.h" #include "LuaComponent.h" #include "LuaSmartRef.h" - #include namespace ui diff --git a/src/lua/PlainCommandInterface.cpp b/src/lua/PlainCommandInterface.cpp new file mode 100644 index 000000000..9763f82fa --- /dev/null +++ b/src/lua/PlainCommandInterface.cpp @@ -0,0 +1,7 @@ +#include "CommandInterface.h" +#include "TPTScriptInterface.h" + +CommandInterface *CommandInterface::Create(GameController * c, GameModel * m) +{ + return new TPTScriptInterface(c, m); +} diff --git a/src/lua/TPTSTypes.h b/src/lua/TPTSTypes.h index 0e27722ad..78f9cd757 100644 --- a/src/lua/TPTSTypes.h +++ b/src/lua/TPTSTypes.h @@ -1,7 +1,4 @@ -#ifndef TPTSTYPES_H_ -#define TPTSTYPES_H_ -#include "Config.h" - +#pragma once #include "common/String.h" #include "gui/interface/Point.h" #include @@ -118,5 +115,3 @@ public: PointType(int pointX, int pointY); ui::Point Value(); }; - -#endif /* TPTSTYPES_H_ */ diff --git a/src/lua/TPTScriptInterface.cpp b/src/lua/TPTScriptInterface.cpp index 2fdd2f1c7..c93fc2c3d 100644 --- a/src/lua/TPTScriptInterface.cpp +++ b/src/lua/TPTScriptInterface.cpp @@ -1,25 +1,15 @@ #include "TPTScriptInterface.h" - -#include -#ifdef MACOSX -#include -#endif -#include -#include - -#include "Config.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 +#include +#include TPTScriptInterface::TPTScriptInterface(GameController * c, GameModel * m): CommandInterface(c, m) { @@ -314,7 +304,7 @@ AnyType TPTScriptInterface::tptS_set(std::deque * words) if (newValue < 0 || newValue >= PT_NUM) { // TODO: add element CAKE to invalidate this - if (!strcasecmp(((StringType)value).Value().ToUtf8().c_str(),"cake")) + if (((StringType)value).Value().ToUpper() == "CAKE") throw GeneralException("Cake is a lie, not an element"); throw GeneralException("Invalid element"); } @@ -567,16 +557,16 @@ AnyType TPTScriptInterface::tptS_reset(std::deque * words) if (resetStr == "pressure") { - for (int nx = 0; nx < XRES/CELL; nx++) - for (int ny = 0; ny < YRES/CELL; ny++) + 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 < XRES/CELL; nx++) - for (int ny = 0; ny < YRES/CELL; ny++) + 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; @@ -610,7 +600,3 @@ AnyType TPTScriptInterface::tptS_quit(std::deque * words) return NumberType(0); } - -TPTScriptInterface::~TPTScriptInterface() { -} - diff --git a/src/lua/TPTScriptInterface.h b/src/lua/TPTScriptInterface.h index e9163c483..79f049b9c 100644 --- a/src/lua/TPTScriptInterface.h +++ b/src/lua/TPTScriptInterface.h @@ -1,13 +1,9 @@ -#ifndef TPTSCRIPTINTERFACE_H_ -#define TPTSCRIPTINTERFACE_H_ -#include "Config.h" - +#pragma once #include "CommandInterface.h" #include "TPTSTypes.h" #include class TPTScriptInterface: public CommandInterface { -protected: AnyType eval(std::deque * words); int parseNumber(String str); AnyType tptS_set(std::deque * words); @@ -22,7 +18,4 @@ public: TPTScriptInterface(GameController * c, GameModel * m); int Command(String command) override; String FormatCommand(String command) override; - virtual ~TPTScriptInterface(); }; - -#endif /* TPTSCRIPTINTERFACE_H_ */ diff --git a/src/lua/meson.build b/src/lua/meson.build index d3ccf57ba..997fe04c4 100644 --- a/src/lua/meson.build +++ b/src/lua/meson.build @@ -5,19 +5,27 @@ luaconsole_files = files( 'LuaCheckbox.cpp', 'LuaCompat.c', 'LuaComponent.cpp', + 'LuaHttp.cpp', 'LuaLabel.cpp', 'LuaProgressBar.cpp', 'LuaScriptInterface.cpp', 'LuaSlider.cpp', + 'LuaSocket.cpp', 'LuaSmartRef.cpp', 'LuaTextbox.cpp', 'LuaWindow.cpp', ) -if enable_http - luaconsole_files += files( - 'LuaTCPSocket.cpp', - ) +if host_platform == 'windows' + luaconsole_files += files('LuaSocketWindows.cpp') +else + luaconsole_files += files('LuaSocketDefault.cpp') endif +if enable_http + luaconsole_files += files('LuaSocketTCPHttp.cpp') +else + luaconsole_files += files('LuaSocketTCPNoHttp.cpp') +endif +conf_data.set('LUACONSOLE', lua_variant != 'none' ? 'true' : 'false') subdir('luascripts') diff --git a/src/meson.build b/src/meson.build index f34b863a7..a5607a331 100644 --- a/src/meson.build +++ b/src/meson.build @@ -1,15 +1,54 @@ -config_template = files('Config.template.h') -subdir('config') +conf_data = configuration_data() + +app_id = get_option('app_id') +mod_id = get_option('mod_id') +is_snapshot = get_option('snapshot') +is_beta = get_option('beta') +is_mod = mod_id > 0 +conf_data.set('X86', is_x86 ? 'true' : 'false') +conf_data.set('BETA', is_beta ? 'true' : 'false') +conf_data.set('MOD_ID', mod_id) +conf_data.set('DEBUG', is_debug ? 'true' : 'false') +conf_data.set('MOD', is_mod ? 'true' : 'false') +conf_data.set('SNAPSHOT', is_snapshot ? 'true' : 'false') +conf_data.set('SNAPSHOT_ID', get_option('snapshot_id')) +conf_data.set('ALLOW_FAKE_NEWER_VERSION', (is_snapshot or is_beta or is_debug or is_mod) ? 'true' : 'false') +conf_data.set('IDENT_PLATFORM', ident_platform) +conf_data.set('IDENT', '@0@-@1@-@2@'.format(host_arch, host_platform, host_libc).to_upper()) + +update_server = get_option('update_server') +conf_data.set('UPDATESERVER', update_server) +conf_data.set('USE_UPDATESERVER', update_server != '' ? 'true' : 'false') + +enforce_https = get_option('enforce_https') +secure_ciphers_only = get_option('secure_ciphers_only') +if not is_debug and not enforce_https + error('refusing to build a release binary without enforcing HTTPS, configure with -Denforce_https=true to fix this error') +endif +conf_data.set('ENFORCE_HTTPS', enforce_https ? 'true' : 'false') +conf_data.set('SECURE_CIPHERS_ONLY', secure_ciphers_only ? 'true' : 'false') + +conf_data.set('IGNORE_UPDATES', get_option('ignore_updates') ? 'true' : 'false') +conf_data.set('SERVER', get_option('server')) +conf_data.set('STATICSERVER', get_option('static_server')) +conf_data.set('APPNAME', get_option('app_name')) +conf_data.set('APPCOMMENT', get_option('app_comment')) +conf_data.set('APPEXE', app_exe) +conf_data.set('APPID', app_id) +conf_data.set('APPDATA', get_option('app_data')) +conf_data.set('APPVENDOR', get_option('app_vendor')) powder_files = files( 'SDLCompat.cpp', - 'Update.cpp', 'PowderToySDL.cpp', + 'PowderToy.cpp', 'lua/CommandInterface.cpp', - 'lua/LuaEvents.cpp', 'lua/TPTScriptInterface.cpp', 'lua/TPTSTypes.cpp', ) +if is_x86 + powder_files += files('X86KillDenormals.cpp') +endif render_files = files( 'PowderToyRenderer.cpp', @@ -17,6 +56,7 @@ render_files = files( font_files = files( 'PowderToyFontEditor.cpp', + 'PowderToySDL.cpp', ) common_files = files( @@ -25,6 +65,11 @@ common_files = files( 'Probability.cpp', ) +if host_platform == 'linux' + powder_files += files('WindowIcon.cpp') + font_files += files('WindowIcon.cpp') +endif + subdir('bson') subdir('bzip2') subdir('client') @@ -34,7 +79,14 @@ subdir('graphics') subdir('gui') if lua_variant != 'none' subdir('lua') + conf_data.set('LUACONSOLE', 'true') +else + powder_files += files( + 'lua/PlainCommandInterface.cpp', + ) + conf_data.set('LUACONSOLE', 'false') endif +subdir('prefs') subdir('resampler') subdir('simulation') subdir('tasks') @@ -66,3 +118,9 @@ configure_file( output: 'ToolNumbers.h', configuration: tools_conf_data ) + +configure_file( + input: 'Config.template.h', + output: 'Config.h', + configuration: conf_data +) diff --git a/src/prefs/GlobalPrefs.h b/src/prefs/GlobalPrefs.h new file mode 100644 index 000000000..dff14f56d --- /dev/null +++ b/src/prefs/GlobalPrefs.h @@ -0,0 +1,11 @@ +#pragma once +#include "Prefs.h" +#include "common/ExplicitSingleton.h" + +class GlobalPrefs : public Prefs, public ExplicitSingleton +{ +public: + GlobalPrefs() : Prefs("powder.pref") + { + } +}; diff --git a/src/prefs/Prefs.cpp b/src/prefs/Prefs.cpp new file mode 100644 index 000000000..3561011da --- /dev/null +++ b/src/prefs/Prefs.cpp @@ -0,0 +1,157 @@ +#include "Prefs.h" +#include "common/platform/Platform.h" +#include "common/tpt-rand.h" +#include "client/User.h" +#include +#include + +Prefs::Prefs(ByteString newPath) : path(newPath) +{ + Read(); +} + +void Prefs::Read() +{ + std::vector data; + if (!Platform::ReadFile(data, path)) + { + return; + } + Json::CharReaderBuilder rbuilder; + std::unique_ptr const reader(rbuilder.newCharReader()); + ByteString errs; + if (!reader->parse(&data[0], &data[0] + data.size(), &root, &errs)) + { + std::cerr << errs << std::endl; + return; + } + backedByFile = true; +} + +void Prefs::ShouldWrite() +{ + shouldWrite = true; + Write(); +} + +void Prefs::Write() +{ + if (deferWriteLevel) + { + return; + } + if (!shouldWrite) + { + return; + } + shouldWrite = false; + Json::StreamWriterBuilder wbuilder; + wbuilder["indentation"] = "\t"; + ByteString data = Json::writeString(wbuilder, root); + if (!Platform::WriteFile(std::vector(data.begin(), data.end()), path)) + { + return; + } + backedByFile = true; +} + +void Prefs::GrabDeferWriteLevel(DeferWriteTag) +{ + deferWriteLevel += 1; +} + +void Prefs::DropDeferWriteLevel(DeferWriteTag) +{ + deferWriteLevel -= 1; + Write(); +} + +Json::Value Prefs::GetJson(const Json::Value &node, ByteString path) +{ + if (node.type() != Json::objectValue) + { + return Json::nullValue; + } + auto split = path.SplitBy('.'); + if (!split) + { + return node[path]; + } + return GetJson(node[split.Before()], split.After()); +} + +void Prefs::SetJson(Json::Value &node, ByteString path, Json::Value value) +{ + if (node.type() != Json::objectValue) + { + node = Json::objectValue; + } + auto split = path.SplitBy('.'); + if (!split) + { + node[path] = value; + return; + } + SetJson(node[split.Before()], split.After(), value); +} + +template<> Json::Value Prefs::Bipacker::Pack (const int &value) { return Json::Value(value); } +template<> int Prefs::Bipacker::Unpack(const Json::Value &value) { return value.asInt(); } + +template<> Json::Value Prefs::Bipacker::Pack (const unsigned int &value) { return Json::Value(value); } +template<> unsigned int Prefs::Bipacker::Unpack(const Json::Value &value) { return value.asUInt(); } + +template<> Json::Value Prefs::Bipacker::Pack (const uint64_t &value) { return Json::Value(Json::UInt64(value)); } +template<> uint64_t Prefs::Bipacker::Unpack(const Json::Value &value) { return value.asUInt64(); } + +template<> Json::Value Prefs::Bipacker::Pack (const float &value) { return Json::Value(value); } +template<> float Prefs::Bipacker::Unpack(const Json::Value &value) { return value.asFloat(); } + +template<> Json::Value Prefs::Bipacker::Pack (const bool &value) { return Json::Value(value); } +template<> bool Prefs::Bipacker::Unpack(const Json::Value &value) { return value.asBool(); } + +template<> Json::Value Prefs::Bipacker::Pack (const ByteString &value) { return Json::Value(value); } +template<> ByteString Prefs::Bipacker::Unpack(const Json::Value &value) { return value.asString(); } + +template<> Json::Value Prefs::Bipacker::Pack (const String &value) { return Json::Value(value.ToUtf8()); } +template<> String Prefs::Bipacker::Unpack(const Json::Value &value) { return ByteString(value.asString()).FromUtf8(); } + +template<> Json::Value Prefs::Bipacker::Pack (const User::Elevation &value) { return Json::Value(User::ElevationToString(value)); } +template<> User::Elevation Prefs::Bipacker::Unpack(const Json::Value &value) { return User::ElevationFromString(value.asString()); } + +template +struct Prefs::Bipacker> +{ + static Json::Value Pack(const std::vector &value); + static std::vector Unpack(const Json::Value &value); +}; + +template +Json::Value Prefs::Bipacker>::Pack(const std::vector &value) +{ + Json::Value array = Json::arrayValue; + for (auto item : value) + { + array.append(Bipacker::Pack(item)); + } + return array; +} + +template +std::vector Prefs::Bipacker>::Unpack(const Json::Value &value) +{ + std::vector array; + if (value.type() != Json::arrayValue) + { + throw std::exception(); + } + for (auto &item : value) + { + array.push_back(Bipacker::Unpack(item)); + } + return array; +} + +template struct Prefs::Bipacker>; +template struct Prefs::Bipacker>; +template struct Prefs::Bipacker>; diff --git a/src/prefs/Prefs.h b/src/prefs/Prefs.h new file mode 100644 index 000000000..a3339f3d8 --- /dev/null +++ b/src/prefs/Prefs.h @@ -0,0 +1,103 @@ +#pragma once +#include "common/String.h" +#include + +class Prefs +{ + struct DeferWriteTag + { + }; + + Json::Value root; + static Json::Value GetJson(const Json::Value &node, ByteString path); + static void SetJson(Json::Value &node, ByteString path, Json::Value value); + + template + struct Bipacker + { + static Json::Value Pack(const Type &value); + static Type Unpack(const Json::Value &value); + }; + + void Read(); + void Write(); + void ShouldWrite(); + unsigned int deferWriteLevel = 0; + bool backedByFile = false; + bool shouldWrite = false; + + ByteString path; + + Prefs(const Prefs &) = delete; + Prefs &operator =(const Prefs &) = delete; + +public: + Prefs(ByteString path); + + template + Type Get(ByteString path, Type defaultValue) const + { + auto value = GetJson(root, path); + if (value != Json::nullValue) + { + try + { + return Bipacker::Unpack(value); + } + catch (const std::exception &e) + { + } + } + return defaultValue; + } + + template + Enum Get(ByteString path, Enum maxValue, Enum defaultValue) const + { + EnumBase value = Get(path, EnumBase(defaultValue)); + if (value < 0 || value >= EnumBase(maxValue)) + { + value = EnumBase(defaultValue); + } + return Enum(value); + } + + template + void Set(ByteString path, Type value) + { + SetJson(root, path, Bipacker::Pack(value)); + ShouldWrite(); + } + + void Clear(ByteString path) + { + SetJson(root, path, Json::nullValue); + ShouldWrite(); + } + + void GrabDeferWriteLevel(DeferWriteTag); + void DropDeferWriteLevel(DeferWriteTag); + + struct DeferWrite + { + Prefs &prefs; + + DeferWrite(const DeferWrite &) = delete; + DeferWrite &operator =(const DeferWrite &) = delete; + + DeferWrite(Prefs &newPrefs) : prefs(newPrefs) + { + prefs.GrabDeferWriteLevel({}); + } + + ~DeferWrite() + { + prefs.DropDeferWriteLevel({}); + } + }; + + bool BackedByFile() const + { + return backedByFile; + } +}; diff --git a/src/prefs/meson.build b/src/prefs/meson.build new file mode 100644 index 000000000..4e7e85db0 --- /dev/null +++ b/src/prefs/meson.build @@ -0,0 +1,3 @@ +powder_files += files( + 'Prefs.cpp', +) diff --git a/src/resampler/resampler.cpp b/src/resampler/resampler.cpp index ba4b67184..2aedb10c3 100644 --- a/src/resampler/resampler.cpp +++ b/src/resampler/resampler.cpp @@ -23,13 +23,7 @@ static inline int resampler_range_check(int v, int h) { (void)h; resampler_assert((v >= 0) && (v < h)); return v; } -#ifndef max - #define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -#ifndef min - #define min(a,b) (((a) < (b)) ? (a) : (b)) -#endif +#include "common/tpt-minmax.h" #ifndef TRUE #define TRUE (1) @@ -41,9 +35,7 @@ static inline int resampler_range_check(int v, int h) { (void)h; resampler_asser #define RESAMPLER_DEBUG 0 -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif +#include "common/tpt-compat.h" // Float to int cast with truncation. static inline int cast_to_int(Resample_Real i) @@ -218,7 +210,7 @@ static Resample_Real catmull_rom_filter(Resample_Real t) static double sinc(double x) { - x = (x * M_PI); + x = (x * TPT_PI_DBL); if ((x < 0.01f) && (x > -0.01f)) return 1.0f + x*x*(-1.0f/6.0f + x*x*1.0f/120.0f); @@ -236,12 +228,12 @@ static Resample_Real clean(double t) //static double blackman_window(double x) //{ -// return .42f + .50f * cos(M_PI*x) + .08f * cos(2.0f*M_PI*x); +// return .42f + .50f * cos(TPT_PI_DBL*x) + .08f * cos(2.0f*TPT_PI_DBL*x); //} static double blackman_exact_window(double x) { - return 0.42659071f + 0.49656062f * cos(M_PI*x) + 0.07684867f * cos(2.0f*M_PI*x); + return 0.42659071f + 0.49656062f * cos(TPT_PI_DBL*x) + 0.07684867f * cos(2.0f*TPT_PI_DBL*x); } #define BLACKMAN_SUPPORT (3.0f) @@ -263,7 +255,7 @@ static Resample_Real gaussian_filter(Resample_Real t) // with blackman window if (t < 0) t = -t; if (t < GAUSSIAN_SUPPORT) - return clean(exp(-2.0f * t * t) * sqrt(2.0f / M_PI) * blackman_exact_window(t / GAUSSIAN_SUPPORT)); + return clean(exp(-2.0f * t * t) * sqrt(2.0f / TPT_PI_DBL) * blackman_exact_window(t / GAUSSIAN_SUPPORT)); else return 0.0f; } diff --git a/src/resampler/resampler.h b/src/resampler/resampler.h index 061b93555..326420208 100644 --- a/src/resampler/resampler.h +++ b/src/resampler/resampler.h @@ -1,11 +1,11 @@ // http://code.google.com/p/imageresampler/ // resampler.h, Separable filtering image rescaler v2.21, Rich Geldreich - richgel99@gmail.com // See unlicense.org text at the bottom of this file. -#ifndef __RESAMPLER_H__ -#define __RESAMPLER_H__ -#include "Config.h" +#pragma once #include +#define HIGH_QUALITY_RESAMPLE + #define RESAMPLER_DEBUG_OPS 0 #define RESAMPLER_DEFAULT_FILTER "lanczos4" @@ -170,9 +170,6 @@ private: return f; } }; - -#endif // __RESAMPLER_H__ - // This is free and unencumbered software released into the public domain. // // Anyone is free to copy, modify, publish, use, compile, sell, or diff --git a/src/simulation/Air.cpp b/src/simulation/Air.cpp index 7e8e49ab6..c8d98ceeb 100644 --- a/src/simulation/Air.cpp +++ b/src/simulation/Air.cpp @@ -1,114 +1,97 @@ #include "Air.h" - -#include -#include - #include "Simulation.h" #include "ElementClasses.h" #include "common/tpt-rand.h" - -/*float kernel[9]; - -float vx[YRES/CELL][XRES/CELL], ovx[YRES/CELL][XRES/CELL]; -float vy[YRES/CELL][XRES/CELL], ovy[YRES/CELL][XRES/CELL]; -float pv[YRES/CELL][XRES/CELL], opv[YRES/CELL][XRES/CELL]; -unsigned char bmap_blockair[YRES/CELL][XRES/CELL]; - -float cb_vx[YRES/CELL][XRES/CELL]; -float cb_vy[YRES/CELL][XRES/CELL]; -float cb_pv[YRES/CELL][XRES/CELL]; -float cb_hv[YRES/CELL][XRES/CELL]; - -float fvx[YRES/CELL][XRES/CELL], fvy[YRES/CELL][XRES/CELL]; - -float hv[YRES/CELL][XRES/CELL], ohv[YRES/CELL][XRES/CELL]; // For Ambient Heat */ +#include +#include void Air::make_kernel(void) //used for velocity { - int i, j; float s = 0.0f; - for (j=-1; j<2; j++) - for (i=-1; i<2; i++) + for (auto j=-1; j<2; j++) + { + for (auto i=-1; i<2; i++) { kernel[(i+1)+3*(j+1)] = expf(-2.0f*(i*i+j*j)); s += kernel[(i+1)+3*(j+1)]; } + } s = 1.0f / s; - for (j=-1; j<2; j++) - for (i=-1; i<2; i++) + for (auto j=-1; j<2; j++) + { + for (auto i=-1; i<2; i++) + { kernel[(i+1)+3*(j+1)] *= s; + } + } } void Air::Clear() { - std::fill(&pv[0][0], &pv[0][0]+((XRES/CELL)*(YRES/CELL)), 0.0f); - std::fill(&vy[0][0], &vy[0][0]+((XRES/CELL)*(YRES/CELL)), 0.0f); - std::fill(&vx[0][0], &vx[0][0]+((XRES/CELL)*(YRES/CELL)), 0.0f); + std::fill(&pv[0][0], &pv[0][0]+NCELL, 0.0f); + std::fill(&vy[0][0], &vy[0][0]+NCELL, 0.0f); + std::fill(&vx[0][0], &vx[0][0]+NCELL, 0.0f); } void Air::ClearAirH() { - std::fill(&hv[0][0], &hv[0][0]+((XRES/CELL)*(YRES/CELL)), ambientAirTemp); + std::fill(&hv[0][0], &hv[0][0]+NCELL, ambientAirTemp); } void Air::update_airh(void) { - int x, y, i, j; - float odh, dh, dx, dy, f, tx, ty; - for (i=0; i0 && y+j0 && x+i0 && y+j0 && x+i=2 && i=2 && j=2 && i=2 && j=2 && x=2 && y=2 && x=2 && y0 && y+j0 && x+i0 && y+j0 && x+i1.0f || dy*advDistanceMult>1.0f) && (tx>=2 && tx=2 && ty1.0f || dy*advDistanceMult>1.0f) && (tx>=2 && tx=2 && tystd::abs(dy)) { stepX = (dx<0.0f) ? 1.f : -1.f; @@ -259,7 +250,8 @@ void Air::update_air(void) } tx = float(x); ty = float(y); - for (step=0; step=2 && i<=XRES/CELL-3 && - j>=2 && j<=YRES/CELL-3) + if (!bmap_blockair[y][x] && i>=2 && i<=XCELLS-3 && + j>=2 && j<=YCELLS-3) { dx *= 1.0f - AIR_VADV; dy *= 1.0f - AIR_VADV; @@ -339,6 +331,7 @@ void Air::update_air(void) ovy[y][x] = dy; opv[y][x] = dp; } + } memcpy(vx, ovx, sizeof(vx)); memcpy(vy, ovy, sizeof(vy)); memcpy(pv, opv, sizeof(pv)); @@ -347,18 +340,19 @@ void Air::update_air(void) void Air::Invert() { - int nx, ny; - for (nx = 0; nx colour, colour2; int goltype; String description; }; diff --git a/src/simulation/CoordStack.h b/src/simulation/CoordStack.h index be0afed59..d2feb1a36 100644 --- a/src/simulation/CoordStack.h +++ b/src/simulation/CoordStack.h @@ -13,10 +13,7 @@ * along with this program. If not, see . */ -#ifndef Simulation_CoordStack_h -#define Simulation_CoordStack_h - -#include "Config.h" // for XRES and YRES +#pragma once #include #include @@ -71,5 +68,3 @@ public: stack_size = 0; } }; - -#endif diff --git a/src/simulation/Editing.cpp b/src/simulation/Editing.cpp new file mode 100644 index 000000000..ad6556705 --- /dev/null +++ b/src/simulation/Editing.cpp @@ -0,0 +1,1134 @@ +#include "Simulation.h" +#include "Sample.h" +#include "SimTool.h" +#include "Snapshot.h" +#include "Air.h" +#include "gravity/Gravity.h" +#include "common/tpt-rand.h" +#include "common/tpt-compat.h" +#include "client/GameSave.h" +#include "ElementClasses.h" +#include "graphics/Renderer.h" +#include "gui/game/Brush.h" +#include +#include + +std::unique_ptr Simulation::CreateSnapshot() +{ + auto snap = std::make_unique(); + snap->AirPressure .insert (snap->AirPressure .begin(), &pv [0][0] , &pv [0][0] + NCELL); + snap->AirVelocityX .insert (snap->AirVelocityX .begin(), &vx [0][0] , &vx [0][0] + NCELL); + snap->AirVelocityY .insert (snap->AirVelocityY .begin(), &vy [0][0] , &vy [0][0] + NCELL); + snap->AmbientHeat .insert (snap->AmbientHeat .begin(), &hv [0][0] , &hv [0][0] + NCELL); + snap->BlockMap .insert (snap->BlockMap .begin(), &bmap[0][0] , &bmap[0][0] + NCELL); + snap->ElecMap .insert (snap->ElecMap .begin(), &emap[0][0] , &emap[0][0] + NCELL); + snap->BlockAir .insert (snap->BlockAir .begin(), &air->bmap_blockair[0][0] , &air->bmap_blockair[0][0] + NCELL); + snap->BlockAirH .insert (snap->BlockAirH .begin(), &air->bmap_blockairh[0][0], &air->bmap_blockairh[0][0] + NCELL); + snap->FanVelocityX .insert (snap->FanVelocityX .begin(), &fvx [0][0] , &fvx [0][0] + NCELL); + snap->FanVelocityY .insert (snap->FanVelocityY .begin(), &fvy [0][0] , &fvy [0][0] + NCELL); + snap->GravVelocityX .insert (snap->GravVelocityX .begin(), &gravx [0] , &gravx [0] + NCELL); + snap->GravVelocityY .insert (snap->GravVelocityY .begin(), &gravy [0] , &gravy [0] + NCELL); + snap->GravValue .insert (snap->GravValue .begin(), &gravp [0] , &gravp [0] + NCELL); + snap->GravMap .insert (snap->GravMap .begin(), &gravmap[0] , &gravmap[0] + NCELL); + snap->Particles .insert (snap->Particles .begin(), &parts [0] , &parts [0] + parts_lastActiveIndex + 1); + snap->PortalParticles.insert (snap->PortalParticles.begin(), &portalp[0][0][0], &portalp[0][0][0] + CHANNELS * 8 * 80); + snap->WirelessData .insert (snap->WirelessData .begin(), &wireless[0][0] , &wireless[0][0] + CHANNELS * 2); + snap->stickmen .insert (snap->stickmen .begin(), &fighters[0] , &fighters[0] + MAX_FIGHTERS); + snap->stickmen .push_back(player2); + snap->stickmen .push_back(player); + snap->signs = signs; + snap->FrameCount = frameCount; + snap->RngState = rng.state(); + return snap; +} + +void Simulation::Restore(const Snapshot &snap) +{ + std::fill(elementCount, elementCount + PT_NUM, 0); + elementRecount = true; + force_stacking_check = true; + for (auto &part : parts) + { + part.type = 0; + } + std::copy(snap.AirPressure .begin(), snap.AirPressure .end(), &pv[0][0] ); + std::copy(snap.AirVelocityX .begin(), snap.AirVelocityX .end(), &vx[0][0] ); + std::copy(snap.AirVelocityY .begin(), snap.AirVelocityY .end(), &vy[0][0] ); + std::copy(snap.AmbientHeat .begin(), snap.AmbientHeat .end(), &hv[0][0] ); + std::copy(snap.BlockMap .begin(), snap.BlockMap .end(), &bmap[0][0] ); + std::copy(snap.ElecMap .begin(), snap.ElecMap .end(), &emap[0][0] ); + std::copy(snap.BlockAir .begin(), snap.BlockAir .end(), &air->bmap_blockair[0][0] ); + std::copy(snap.BlockAirH .begin(), snap.BlockAirH .end(), &air->bmap_blockairh[0][0]); + std::copy(snap.FanVelocityX .begin(), snap.FanVelocityX .end(), &fvx[0][0] ); + std::copy(snap.FanVelocityY .begin(), snap.FanVelocityY .end(), &fvy[0][0] ); + if (grav->IsEnabled()) + { + grav->Clear(); + std::copy(snap.GravVelocityX.begin(), snap.GravVelocityX.end(), &gravx [0] ); + std::copy(snap.GravVelocityY.begin(), snap.GravVelocityY.end(), &gravy [0] ); + std::copy(snap.GravValue .begin(), snap.GravValue .end(), &gravp [0] ); + std::copy(snap.GravMap .begin(), snap.GravMap .end(), &gravmap[0] ); + } + std::copy(snap.Particles .begin(), snap.Particles .end(), &parts[0] ); + std::copy(snap.PortalParticles.begin(), snap.PortalParticles.end(), &portalp[0][0][0]); + std::copy(snap.WirelessData .begin(), snap.WirelessData .end(), &wireless[0][0] ); + std::copy(snap.stickmen .begin(), snap.stickmen.end() - 2 , &fighters[0] ); + player = snap.stickmen[snap.stickmen.size() - 1]; + player2 = snap.stickmen[snap.stickmen.size() - 2]; + signs = snap.signs; + frameCount = snap.FrameCount; + rng.state(snap.RngState); + parts_lastActiveIndex = NPART - 1; + RecalcFreeParticles(false); + gravWallChanged = true; +} + +void Simulation::clear_area(int area_x, int area_y, int area_w, int area_h) +{ + float fx = area_x-.5f, fy = area_y-.5f; + for (int i = 0; i <= parts_lastActiveIndex; i++) + { + if (parts[i].type) + if (parts[i].x >= fx && parts[i].x <= fx+area_w+1 && parts[i].y >= fy && parts[i].y <= fy+area_h+1) + kill_part(i); + } + int cx1 = area_x/CELL, cy1 = area_y/CELL, cx2 = (area_x+area_w)/CELL, cy2 = (area_y+area_h)/CELL; + for (int y = cy1; y <= cy2; y++) + { + for (int x = cx1; x <= cx2; x++) + { + if (bmap[y][x] == WL_GRAV) + gravWallChanged = true; + bmap[y][x] = 0; + emap[y][x] = 0; + } + } + for( int i = signs.size()-1; i >= 0; i--) + { + if (signs[i].text.length() && signs[i].x >= area_x && signs[i].y >= area_y && signs[i].x <= area_x+area_w && signs[i].y <= area_y+area_h) + { + signs.erase(signs.begin()+i); + } + } +} + +SimulationSample Simulation::GetSample(int x, int y) +{ + SimulationSample sample; + sample.PositionX = x; + sample.PositionY = y; + if (x >= 0 && x < XRES && y >= 0 && y < YRES) + { + if (photons[y][x]) + { + sample.particle = parts[ID(photons[y][x])]; + sample.ParticleID = ID(photons[y][x]); + } + else if (pmap[y][x]) + { + sample.particle = parts[ID(pmap[y][x])]; + sample.ParticleID = ID(pmap[y][x]); + } + if (bmap[y/CELL][x/CELL]) + { + sample.WallType = bmap[y/CELL][x/CELL]; + } + sample.AirPressure = pv[y/CELL][x/CELL]; + sample.AirTemperature = hv[y/CELL][x/CELL]; + sample.AirVelocityX = vx[y/CELL][x/CELL]; + sample.AirVelocityY = vy[y/CELL][x/CELL]; + + if(grav->IsEnabled()) + { + sample.Gravity = gravp[(y/CELL)*XCELLS+(x/CELL)]; + sample.GravityVelocityX = gravx[(y/CELL)*XCELLS+(x/CELL)]; + sample.GravityVelocityY = gravy[(y/CELL)*XCELLS+(x/CELL)]; + } + } + else + sample.isMouseInSim = false; + + sample.NumParts = NUM_PARTS; + return sample; +} + +void Simulation::SetDecoSpace(int newDecoSpace) +{ + switch (newDecoSpace) + { + case 0: // sRGB + default: // anything stupid + deco_space = 0; + break; + + case 1: // linear + deco_space = 1; + break; + + case 2: // Gamma = 2.2 + deco_space = 2; + break; + + case 3: // Gamma = 1.8 + deco_space = 3; + break; + } +} + +int Simulation::Tool(int x, int y, int tool, int brushX, int brushY, float strength) +{ + Particle * cpart = NULL; + int r; + if ((r = pmap[y][x])) + cpart = &(parts[ID(r)]); + else if ((r = photons[y][x])) + cpart = &(parts[ID(r)]); + return tools[tool].Perform(this, cpart, x, y, brushX, brushY, strength); +} + +int Simulation::CreateWalls(int x, int y, int rx, int ry, int wall, Brush const *cBrush) +{ + if(cBrush) + { + rx = cBrush->GetRadius().X; + ry = cBrush->GetRadius().Y; + } + + ry = ry/CELL; + rx = rx/CELL; + x = x/CELL; + y = y/CELL; + x -= rx; + y -= ry; + for (int wallX = x; wallX <= x+rx+rx; wallX++) + { + for (int wallY = y; wallY <= y+ry+ry; wallY++) + { + if (wallX >= 0 && wallX < XCELLS && wallY >= 0 && wallY < YCELLS) + { + if (wall == WL_FAN) + { + fvx[wallY][wallX] = 0.0f; + fvy[wallY][wallX] = 0.0f; + } + else if (wall == WL_STREAM) + { + wallX = x + rx; + wallY = y + ry; + //streamlines can't be drawn next to each other + for (int tempY = wallY-1; tempY < wallY+2; tempY++) + for (int tempX = wallX-1; tempX < wallX+2; tempX++) + { + if (tempX >= 0 && tempX < XCELLS && tempY >= 0 && tempY < YCELLS && bmap[tempY][tempX] == WL_STREAM) + return 1; + } + } + if (wall == WL_GRAV || bmap[wallY][wallX] == WL_GRAV) + gravWallChanged = true; + + if (wall == WL_ERASEALL) + { + for (int i = 0; i < CELL; i++) + for (int j = 0; j < CELL; j++) + { + delete_part(wallX*CELL+i, wallY*CELL+j); + } + for (int i = signs.size()-1; i >= 0; i--) + if (signs[i].x >= wallX*CELL && signs[i].y >= wallY*CELL && signs[i].x <= (wallX+1)*CELL && signs[i].y <= (wallY+1)*CELL) + signs.erase(signs.begin()+i); + bmap[wallY][wallX] = 0; + } + else + bmap[wallY][wallX] = wall; + } + } + } + return 1; +} + +void Simulation::CreateWallLine(int x1, int y1, int x2, int y2, int rx, int ry, int wall, Brush const *cBrush) +{ + int x, y, dx, dy, sy; + bool reverseXY = abs(y2-y1) > abs(x2-x1); + float e = 0.0f, de; + if (reverseXY) + { + y = x1; + x1 = y1; + y1 = y; + y = x2; + x2 = y2; + y2 = y; + } + if (x1 > x2) + { + y = x1; + x1 = x2; + x2 = y; + y = y1; + y1 = y2; + y2 = y; + } + dx = x2 - x1; + dy = abs(y2 - y1); + de = dx ? dy/(float)dx : 0.0f; + y = y1; + sy = (y1= 0.5f) + { + y += sy; + if ((y1=y2)) + { + if (reverseXY) + CreateWalls(y, x, rx, ry, wall, cBrush); + else + CreateWalls(x, y, rx, ry, wall, cBrush); + } + e -= 1.0f; + } + } +} + +void Simulation::CreateWallBox(int x1, int y1, int x2, int y2, int wall) +{ + int i, j; + if (x1>x2) + { + i = x2; + x2 = x1; + x1 = i; + } + if (y1>y2) + { + j = y2; + y2 = y1; + y1 = j; + } + for (j=y1; j<=y2; j++) + for (i=x1; i<=x2; i++) + CreateWalls(i, j, 0, 0, wall, NULL); +} + +int Simulation::FloodWalls(int x, int y, int wall, int bm) +{ + int x1, x2, dy = CELL; + if (bm==-1) + { + if (wall==WL_ERASE || wall==WL_ERASEALL) + { + bm = bmap[y/CELL][x/CELL]; + if (!bm) + return 0; + } + else + bm = 0; + } + + if (bmap[y/CELL][x/CELL]!=bm) + return 1; + + // go left as far as possible + x1 = x2 = x; + while (x1>=CELL) + { + if (bmap[y/CELL][(x1-1)/CELL]!=bm) + { + break; + } + x1--; + } + while (x2=CELL) + for (x=x1; x<=x2; x++) + if (bmap[(y-dy)/CELL][x/CELL]==bm) + if (!FloodWalls(x, y-dy, wall, bm)) + return 0; + if (y= XRES || y >= YRES) + { + return 0; + } + + if (flags & REPLACE_MODE) + { + // if replace whatever and there's something to replace + // or replace X and there's a non-energy particle on top with type X + // or replace X and there's an energy particle on top with type X + if ((!replaceModeSelected && (photons[y][x] || pmap[y][x])) || + (!photons[y][x] && pmap[y][x] && TYP(pmap[y][x]) == replaceModeSelected) || + (photons[y][x] && TYP(photons[y][x]) == replaceModeSelected)) + { + if (c) + create_part(photons[y][x] ? ID(photons[y][x]) : ID(pmap[y][x]), x, y, TYP(c), ID(c)); + else + delete_part(x, y); + } + return 0; + } + else if (!c) + { + delete_part(x, y); + return 0; + } + else if (flags & SPECIFIC_DELETE) + { + // if delete whatever and there's something to delete + // or delete X and there's a non-energy particle on top with type X + // or delete X and there's an energy particle on top with type X + if ((!replaceModeSelected && (photons[y][x] || pmap[y][x])) || + (!photons[y][x] && pmap[y][x] && TYP(pmap[y][x]) == replaceModeSelected) || + (photons[y][x] && TYP(photons[y][x]) == replaceModeSelected)) + { + delete_part(x, y); + } + return 0; + } + else + { + return (create_part(-2, x, y, TYP(c), ID(c)) == -1); + } + + // I'm sure at least one compiler exists that would complain if this wasn't here + return 0; +} + +int Simulation::GetParticleType(ByteString type) +{ + type = type.ToUpper(); + + // alternative names for some elements + if (byteStringEqualsLiteral(type, "C4")) + return PT_PLEX; + else if (byteStringEqualsLiteral(type, "C5")) + return PT_C5; + else if (byteStringEqualsLiteral(type, "NONE")) + return PT_NONE; + for (int i = 1; i < PT_NUM; i++) + { + if (elements[i].Name.size() && elements[i].Enabled && type == elements[i].Name.ToUtf8().ToUpper()) + { + return i; + } + } + return -1; +} + +void Simulation::ApplyDecoration(int x, int y, int colR_, int colG_, int colB_, int colA_, int mode) +{ + int rp; + float tr, tg, tb, ta, colR = float(colR_), colG = float(colG_), colB = float(colB_), colA = float(colA_); + float strength = 0.01f; + rp = pmap[y][x]; + if (!rp) + rp = photons[y][x]; + if (!rp) + return; + + ta = float((parts[ID(rp)].dcolour>>24)&0xFF); + tr = float((parts[ID(rp)].dcolour>>16)&0xFF); + tg = float((parts[ID(rp)].dcolour>>8)&0xFF); + tb = float((parts[ID(rp)].dcolour)&0xFF); + + ta /= 255.0f; tr /= 255.0f; tg /= 255.0f; tb /= 255.0f; + colR /= 255.0f; colG /= 255.0f; colB /= 255.0f; colA /= 255.0f; + + if (mode == DECO_DRAW) + { + ta = colA; + tr = colR; + tg = colG; + tb = colB; + } + else if (mode == DECO_CLEAR) + { + ta = tr = tg = tb = 0.0f; + } + else if (mode == DECO_ADD) + { + //ta += (colA*strength)*colA; + tr += (colR*strength)*colA; + tg += (colG*strength)*colA; + tb += (colB*strength)*colA; + } + else if (mode == DECO_SUBTRACT) + { + //ta -= (colA*strength)*colA; + tr -= (colR*strength)*colA; + tg -= (colG*strength)*colA; + tb -= (colB*strength)*colA; + } + else if (mode == DECO_MULTIPLY) + { + tr *= 1.0f+(colR*strength)*colA; + tg *= 1.0f+(colG*strength)*colA; + tb *= 1.0f+(colB*strength)*colA; + } + else if (mode == DECO_DIVIDE) + { + tr /= 1.0f+(colR*strength)*colA; + tg /= 1.0f+(colG*strength)*colA; + tb /= 1.0f+(colB*strength)*colA; + } + else if (mode == DECO_SMUDGE) + { + if (x >= CELL && x < XRES-CELL && y >= CELL && y < YRES-CELL) + { + float tas = 0.0f, trs = 0.0f, tgs = 0.0f, tbs = 0.0f; + + int rx, ry; + float num = 0; + for (rx=-2; rx<3; rx++) + for (ry=-2; ry<3; ry++) + { + if (abs(rx)+abs(ry) > 2 && TYP(pmap[y+ry][x+rx]) && parts[ID(pmap[y+ry][x+rx])].dcolour) + { + Particle part = parts[ID(pmap[y+ry][x+rx])]; + num += 1.0f; + float pa = ((float)((part.dcolour>>24)&0xFF)) / 255.f; + float pr = ((float)((part.dcolour>>16)&0xFF)) / 255.f; + float pg = ((float)((part.dcolour>> 8)&0xFF)) / 255.f; + float pb = ((float)((part.dcolour )&0xFF)) / 255.f; + switch (deco_space) + { + case 0: // sRGB + pa = (pa <= 0.04045f) ? (pa / 12.92f) : pow((pa + 0.055f) / 1.055f, 2.4f); + pr = (pr <= 0.04045f) ? (pr / 12.92f) : pow((pr + 0.055f) / 1.055f, 2.4f); + pg = (pg <= 0.04045f) ? (pg / 12.92f) : pow((pg + 0.055f) / 1.055f, 2.4f); + pb = (pb <= 0.04045f) ? (pb / 12.92f) : pow((pb + 0.055f) / 1.055f, 2.4f); + break; + + case 1: // linear + break; + + case 2: // Gamma = 2.2 + pa = pow(pa, 2.2f); + pr = pow(pr, 2.2f); + pg = pow(pg, 2.2f); + pb = pow(pb, 2.2f); + break; + + case 3: // Gamma = 1.8 + pa = pow(pa, 1.8f); + pr = pow(pr, 1.8f); + pg = pow(pg, 1.8f); + pb = pow(pb, 1.8f); + break; + } + tas += pa; + trs += pr; + tgs += pg; + tbs += pb; + } + } + if (num == 0) + return; + ta = tas / num; + tr = trs / num; + tg = tgs / num; + tb = tbs / num; + switch (deco_space) + { + case 0: // sRGB + ta = (ta <= 0.0031308f) ? (ta * 12.92f) : (1.055f * pow(ta, 1.f / 2.4f) - 0.055f); + tr = (tr <= 0.0031308f) ? (tr * 12.92f) : (1.055f * pow(tr, 1.f / 2.4f) - 0.055f); + tg = (tg <= 0.0031308f) ? (tg * 12.92f) : (1.055f * pow(tg, 1.f / 2.4f) - 0.055f); + tb = (tb <= 0.0031308f) ? (tb * 12.92f) : (1.055f * pow(tb, 1.f / 2.4f) - 0.055f); + break; + + case 1: // linear + break; + + case 2: // Gamma = 2.2 + ta = pow(ta, 1.f / 2.2f); + tr = pow(tr, 1.f / 2.2f); + tg = pow(tg, 1.f / 2.2f); + tb = pow(tb, 1.f / 2.2f); + break; + + case 3: // Gamma = 1.8 + ta = pow(ta, 1.f / 1.8f); + tr = pow(tr, 1.f / 1.8f); + tg = pow(tg, 1.f / 1.8f); + tb = pow(tb, 1.f / 1.8f); + break; + } + if (!parts[ID(rp)].dcolour) + ta -= 3/255.0f; + } + } + + ta *= 255.0f; tr *= 255.0f; tg *= 255.0f; tb *= 255.0f; + ta += .5f; tr += .5f; tg += .5f; tb += .5f; + + colA_ = int(ta); + colR_ = int(tr); + colG_ = int(tg); + colB_ = int(tb); + + if(colA_ > 255) + colA_ = 255; + else if(colA_ < 0) + colA_ = 0; + if(colR_ > 255) + colR_ = 255; + else if(colR_ < 0) + colR_ = 0; + if(colG_ > 255) + colG_ = 255; + else if(colG_ < 0) + colG_ = 0; + if(colB_ > 255) + colB_ = 255; + else if(colB_ < 0) + colB_ = 0; + parts[ID(rp)].dcolour = ((colA_<<24)|(colR_<<16)|(colG_<<8)|colB_); +} + +void Simulation::ApplyDecorationPoint(int positionX, int positionY, int colR, int colG, int colB, int colA, int mode, Brush const &cBrush) +{ + for (ui::Point off : cBrush) + { + ui::Point coords = ui::Point(positionX, positionY) + off; + if (coords.X >= 0 && coords.Y >= 0 && coords.X < XRES && coords.Y < YRES) + ApplyDecoration(coords.X, coords.Y, colR, colG, colB, colA, mode); + } +} + +void Simulation::ApplyDecorationLine(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode, Brush const &cBrush) +{ + bool reverseXY = abs(y2-y1) > abs(x2-x1); + int x, y, dx, dy, sy, rx = 0, ry = 0; + float e = 0.0f, de; + + rx = cBrush.GetRadius().X; + ry = cBrush.GetRadius().Y; + + if (reverseXY) + { + y = x1; + x1 = y1; + y1 = y; + y = x2; + x2 = y2; + y2 = y; + } + if (x1 > x2) + { + y = x1; + x1 = x2; + x2 = y; + y = y1; + y1 = y2; + y2 = y; + } + dx = x2 - x1; + dy = abs(y2 - y1); + de = dx ? dy/(float)dx : 0.0f; + y = y1; + sy = (y1= 0.5f) + { + y += sy; + if (!(rx+ry)) + { + if (reverseXY) + ApplyDecorationPoint(y, x, colR, colG, colB, colA, mode, cBrush); + else + ApplyDecorationPoint(x, y, colR, colG, colB, colA, mode, cBrush); + } + e -= 1.0f; + } + } +} + +void Simulation::ApplyDecorationBox(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode) +{ + int i, j; + + if (x1>x2) + { + i = x2; + x2 = x1; + x1 = i; + } + if (y1>y2) + { + j = y2; + y2 = y1; + y1 = j; + } + for (j=y1; j<=y2; j++) + for (i=x1; i<=x2; i++) + ApplyDecoration(i, j, colR, colG, colB, colA, mode); +} + +bool Simulation::ColorCompare(Renderer *ren, int x, int y, int replaceR, int replaceG, int replaceB) +{ + auto pix = RGB::Unpack(ren->GetPixel({ x, y })); + int r = pix.Red; + int g = pix.Green; + int b = pix.Blue; + int diff = std::abs(replaceR-r) + std::abs(replaceG-g) + std::abs(replaceB-b); + return diff < 15; +} + +void Simulation::ApplyDecorationFill(Renderer *ren, int x, int y, int colR, int colG, int colB, int colA, int replaceR, int replaceG, int replaceB) +{ + int x1, x2; + char *bitmap = (char*)malloc(XRES*YRES); //Bitmap for checking + if (!bitmap) + return; + memset(bitmap, 0, XRES*YRES); + + if (!ColorCompare(ren, x, y, replaceR, replaceG, replaceB)) { + free(bitmap); + return; + } + + try + { + CoordStack& cs = getCoordStackSingleton(); + cs.clear(); + + cs.push(x, y); + do + { + cs.pop(x, y); + x1 = x2 = x; + // go left as far as possible + while (x1>0) + { + if (bitmap[(x1-1)+y*XRES] || !ColorCompare(ren, x1-1, y, replaceR, replaceG, replaceB)) + { + break; + } + x1--; + } + // go right as far as possible + while (x2= 1) + for (x=x1; x<=x2; x++) + if (!bitmap[x+(y-1)*XRES] && ColorCompare(ren, x, y-1, replaceR, replaceG, replaceB)) + cs.push(x, y-1); + + if (y < YRES-1) + for (x=x1; x<=x2; x++) + if (!bitmap[x+(y+1)*XRES] && ColorCompare(ren, x, y+1, replaceR, replaceG, replaceB)) + cs.push(x, y+1); + } while (cs.getSize() > 0); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + free(bitmap); + return; + } + free(bitmap); +} + +int Simulation::ToolBrush(int positionX, int positionY, int tool, Brush const &cBrush, float strength) +{ + for (ui::Point off : cBrush) + { + ui::Point coords = ui::Point(positionX, positionY) + off; + if (coords.X >= 0 && coords.Y >= 0 && coords.X < XRES && coords.Y < YRES) + Tool(coords.X, coords.Y, tool, positionX, positionY, strength); + } + return 0; +} + +void Simulation::ToolLine(int x1, int y1, int x2, int y2, int tool, Brush const &cBrush, float strength) +{ + bool reverseXY = abs(y2-y1) > abs(x2-x1); + int x, y, dx, dy, sy, rx = cBrush.GetRadius().X, ry = cBrush.GetRadius().Y; + float e = 0.0f, de; + if (reverseXY) + { + y = x1; + x1 = y1; + y1 = y; + y = x2; + x2 = y2; + y2 = y; + } + if (x1 > x2) + { + y = x1; + x1 = x2; + x2 = y; + y = y1; + y1 = y2; + y2 = y; + } + dx = x2 - x1; + dy = abs(y2 - y1); + de = dx ? dy/(float)dx : 0.0f; + y = y1; + sy = (y1= 0.5f) + { + y += sy; + if (!(rx+ry) && ((y1=y2))) + { + if (reverseXY) + ToolBrush(y, x, tool, cBrush, strength); + else + ToolBrush(x, y, tool, cBrush, strength); + } + e -= 1.0f; + } + } +} + +void Simulation::ToolBox(int x1, int y1, int x2, int y2, int tool, float strength) +{ + int brushX, brushY; + brushX = ((x1 + x2) / 2); + brushY = ((y1 + y2) / 2); + int i, j; + if (x1>x2) + { + i = x2; + x2 = x1; + x1 = i; + } + if (y1>y2) + { + j = y2; + y2 = y1; + y1 = j; + } + for (j=y1; j<=y2; j++) + for (i=x1; i<=x2; i++) + Tool(i, j, tool, brushX, brushY, strength); +} + +int Simulation::CreateParts(int positionX, int positionY, int c, Brush const &cBrush, int flags) +{ + if (flags == -1) + flags = replaceModeFlags; + int radiusX = cBrush.GetRadius().X, radiusY = cBrush.GetRadius().Y; + + // special case for LIGH + if (c == PT_LIGH) + { + if (currentTick < lightningRecreate) + return 1; + int newlife = radiusX + radiusY; + if (newlife > 55) + newlife = 55; + c = PMAP(newlife, c); + lightningRecreate = currentTick + std::max(newlife / 4, 1); + return CreatePartFlags(positionX, positionY, c, flags); + } + else if (c == PT_TESC) + { + int newtmp = (radiusX*4+radiusY*4+7); + if (newtmp > 300) + newtmp = 300; + c = PMAP(newtmp, c); + } + + for (ui::Point off : cBrush) + { + ui::Point coords = ui::Point(positionX, positionY) + off; + if (coords.X >= 0 && coords.Y >= 0 && coords.X < XRES && coords.Y < YRES) + CreatePartFlags(coords.X, coords.Y, c, flags); + } + return 0; +} + +int Simulation::CreateParts(int x, int y, int rx, int ry, int c, int flags) +{ + bool created = false; + + if (flags == -1) + flags = replaceModeFlags; + + // special case for LIGH + if (c == PT_LIGH) + { + if (currentTick < lightningRecreate) + return 1; + int newlife = rx + ry; + if (newlife > 55) + newlife = 55; + c = PMAP(newlife, c); + lightningRecreate = currentTick + std::max(newlife / 4, 1); + rx = ry = 0; + } + else if (c == PT_TESC) + { + int newtmp = (rx*4+ry*4+7); + if (newtmp > 300) + newtmp = 300; + c = PMAP(newtmp, c); + } + + for (int j = -ry; j <= ry; j++) + for (int i = -rx; i <= rx; i++) + if (CreatePartFlags(x+i, y+j, c, flags)) + created = true; + return !created; +} + +void Simulation::CreateLine(int x1, int y1, int x2, int y2, int c, Brush const &cBrush, int flags) +{ + int x, y, dx, dy, sy, rx = cBrush.GetRadius().X, ry = cBrush.GetRadius().Y; + bool reverseXY = abs(y2-y1) > abs(x2-x1); + float e = 0.0f, de; + if (reverseXY) + { + y = x1; + x1 = y1; + y1 = y; + y = x2; + x2 = y2; + y2 = y; + } + if (x1 > x2) + { + y = x1; + x1 = x2; + x2 = y; + y = y1; + y1 = y2; + y2 = y; + } + dx = x2 - x1; + dy = abs(y2 - y1); + de = dx ? dy/(float)dx : 0.0f; + y = y1; + sy = (y1= 0.5f) + { + y += sy; + if (!(rx+ry) && ((y1=y2))) + { + if (reverseXY) + CreateParts(y, x, c, cBrush, flags); + else + CreateParts(x, y, c, cBrush, flags); + } + e -= 1.0f; + } + } +} + +void Simulation::CreateBox(int x1, int y1, int x2, int y2, int c, int flags) +{ + int i, j; + if (x1>x2) + { + i = x2; + x2 = x1; + x1 = i; + } + if (y1>y2) + { + j = y2; + y2 = y1; + y1 = j; + } + for (j=y2; j>=y1; j--) + for (i=x1; i<=x2; i++) + CreateParts(i, j, 0, 0, c, flags); +} + +int Simulation::FloodParts(int x, int y, int fullc, int cm, int flags) +{ + int c = TYP(fullc); + int x1, x2, dy = (c(new char[XRES * YRES]); + char *bitmap = bitmapPtr.get(); + std::fill(&bitmap[0], &bitmap[0] + XRES * YRES, 0); + + if (cm==-1) + { + //if initial flood point is out of bounds, do nothing + if (c != 0 && (x < CELL || x >= XRES-CELL || y < CELL || y >= YRES-CELL || c == PT_SPRK)) + return 1; + else if (x < 0 || x >= XRES || y < 0 || y >= YRES) + return 1; + + if (c == 0) + { + cm = TYP(pmap[y][x]); + if (!cm) + { + cm = TYP(photons[y][x]); + if (!cm) + { + if (bmap[y/CELL][x/CELL]) + return FloodWalls(x, y, WL_ERASE, -1); + else + return -1; + } + } + } + else + cm = 0; + } + + if (c != 0 && IsWallBlocking(x, y, c)) + return 1; + + if (!FloodFillPmapCheck(x, y, cm)) + return 1; + + coord_stack = (short unsigned int (*)[2])malloc(sizeof(unsigned short)*2*coord_stack_limit); + coord_stack[coord_stack_size][0] = x; + coord_stack[coord_stack_size][1] = y; + coord_stack_size++; + + do + { + coord_stack_size--; + x = coord_stack[coord_stack_size][0]; + y = coord_stack[coord_stack_size][1]; + x1 = x2 = x; + // go left as far as possible + while (c?x1>CELL:x1>0) + { + if (bitmap[(y * XRES) + x1 - 1] || !FloodFillPmapCheck(x1-1, y, cm) || (c != 0 && IsWallBlocking(x1-1, y, c))) + { + break; + } + x1--; + } + // go right as far as possible + while (c?x2=CELL+dy:y>=dy) + for (x=x1; x<=x2; x++) + if (!bitmap[((y - dy) * XRES) + x] && FloodFillPmapCheck(x, y-dy, cm) && (c == 0 || !IsWallBlocking(x, y-dy, c))) + { + coord_stack[coord_stack_size][0] = x; + coord_stack[coord_stack_size][1] = y-dy; + coord_stack_size++; + if (coord_stack_size>=coord_stack_limit) + { + free(coord_stack); + return -1; + } + } + + if (c?y=coord_stack_limit) + { + free(coord_stack); + return -1; + } + } + } while (coord_stack_size>0); + free(coord_stack); + return created_something; +} diff --git a/src/simulation/Element.cpp b/src/simulation/Element.cpp index 70a3a5b17..a80c098d0 100644 --- a/src/simulation/Element.cpp +++ b/src/simulation/Element.cpp @@ -4,7 +4,7 @@ Element::Element(): Identifier("DEFAULT_INVALID"), Name(""), - Colour(PIXPACK(0xFF00FF)), + Colour(0xFF00FF_rgb), MenuVisible(0), MenuSection(0), Enabled(0), @@ -31,6 +31,7 @@ Element::Element(): Description("No description"), Properties(TYPE_SOLID), + CarriesTypeIn(0), LowPressure(IPL), LowPressureTransition(NT), @@ -73,6 +74,7 @@ std::vector const &Element::GetProperties() { "Explosive", StructProperty::Integer, offsetof(Element, Explosive ) }, { "Meltable", StructProperty::Integer, offsetof(Element, Meltable ) }, { "Hardness", StructProperty::Integer, offsetof(Element, Hardness ) }, + { "CarriesTypeIn", StructProperty::UInteger, offsetof(Element, CarriesTypeIn ) }, { "Weight", StructProperty::Integer, offsetof(Element, Weight ) }, { "Temperature", StructProperty::Float, offsetof(Element, DefaultProperties.temp ) }, { "HeatConduct", StructProperty::UChar, offsetof(Element, HeatConduct ) }, @@ -103,15 +105,15 @@ int Element::legacyUpdate(UPDATE_FUNC_ARGS) { r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_WATR||TYP(r)==PT_DSTW||TYP(r)==PT_SLTW) && RNG::Ref().chance(1, 1000)) + if ((TYP(r)==PT_WATR||TYP(r)==PT_DSTW||TYP(r)==PT_SLTW) && sim->rng.chance(1, 1000)) { sim->part_change_type(i,x,y,PT_WATR); sim->part_change_type(ID(r),x+rx,y+ry,PT_WATR); } - if ((TYP(r)==PT_ICEI || TYP(r)==PT_SNOW) && RNG::Ref().chance(1, 1000)) + if ((TYP(r)==PT_ICEI || TYP(r)==PT_SNOW) && sim->rng.chance(1, 1000)) { sim->part_change_type(i,x,y,PT_WATR); - if (RNG::Ref().chance(1, 1000)) + if (sim->rng.chance(1, 1000)) sim->part_change_type(ID(r),x+rx,y+ry,PT_WATR); } } @@ -125,7 +127,7 @@ int Element::legacyUpdate(UPDATE_FUNC_ARGS) { r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_FIRE || TYP(r)==PT_LAVA) && RNG::Ref().chance(1, 10)) + if ((TYP(r)==PT_FIRE || TYP(r)==PT_LAVA) && sim->rng.chance(1, 10)) { sim->part_change_type(i,x,y,PT_WTRV); } @@ -140,9 +142,9 @@ int Element::legacyUpdate(UPDATE_FUNC_ARGS) { r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_FIRE || TYP(r)==PT_LAVA) && RNG::Ref().chance(1, 10)) + if ((TYP(r)==PT_FIRE || TYP(r)==PT_LAVA) && sim->rng.chance(1, 10)) { - if (RNG::Ref().chance(1, 4)) + if (sim->rng.chance(1, 4)) sim->part_change_type(i,x,y,PT_SALT); else sim->part_change_type(i,x,y,PT_WTRV); @@ -158,7 +160,7 @@ int Element::legacyUpdate(UPDATE_FUNC_ARGS) { r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_FIRE || TYP(r)==PT_LAVA) && RNG::Ref().chance(1, 10)) + if ((TYP(r)==PT_FIRE || TYP(r)==PT_LAVA) && sim->rng.chance(1, 10)) { sim->part_change_type(i,x,y,PT_WTRV); } @@ -172,7 +174,7 @@ int Element::legacyUpdate(UPDATE_FUNC_ARGS) { r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_WATR || TYP(r)==PT_DSTW) && RNG::Ref().chance(1, 1000)) + if ((TYP(r)==PT_WATR || TYP(r)==PT_DSTW) && sim->rng.chance(1, 1000)) { sim->part_change_type(i,x,y,PT_ICEI); sim->part_change_type(ID(r),x+rx,y+ry,PT_ICEI); @@ -187,12 +189,12 @@ int Element::legacyUpdate(UPDATE_FUNC_ARGS) { r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_WATR || TYP(r)==PT_DSTW) && RNG::Ref().chance(1, 1000)) + if ((TYP(r)==PT_WATR || TYP(r)==PT_DSTW) && sim->rng.chance(1, 1000)) { sim->part_change_type(i,x,y,PT_ICEI); sim->part_change_type(ID(r),x+rx,y+ry,PT_ICEI); } - if ((TYP(r)==PT_WATR || TYP(r)==PT_DSTW) && RNG::Ref().chance(3, 200)) + if ((TYP(r)==PT_WATR || TYP(r)==PT_DSTW) && sim->rng.chance(3, 200)) sim->part_change_type(i,x,y,PT_WATR); } } @@ -205,7 +207,7 @@ int Element::legacyUpdate(UPDATE_FUNC_ARGS) { if (t==PT_DESL && sim->pv[y/CELL][x/CELL]>12.0f) { sim->part_change_type(i,x,y,PT_FIRE); - parts[i].life = RNG::Ref().between(120, 169); + parts[i].life = sim->rng.between(120, 169); } return 0; } diff --git a/src/simulation/Element.h b/src/simulation/Element.h index f5be4fe0a..30faf37a9 100644 --- a/src/simulation/Element.h +++ b/src/simulation/Element.h @@ -1,6 +1,6 @@ -#ifndef ELEMENTCLASS_H -#define ELEMENTCLASS_H - +#pragma once +#include +#include "common/Vec2.h" #include "graphics/Pixel.h" #include "ElementDefs.h" #include "Particle.h" @@ -15,7 +15,7 @@ class Element public: ByteString Identifier; String Name; - pixel Colour; + RGB Colour; int MenuVisible; int MenuSection; int Enabled; @@ -38,6 +38,7 @@ public: unsigned char HeatConduct; String Description; unsigned int Properties; + unsigned int CarriesTypeIn; float LowPressure; int LowPressureTransition; @@ -57,7 +58,7 @@ public: bool (*CtypeDraw) (CTYPEDRAW_FUNC_ARGS); - VideoBuffer * (*IconGenerator)(int, int, int); + std::unique_ptr (*IconGenerator)(int, Vec2); Particle DefaultProperties; @@ -76,5 +77,3 @@ public: #include "ElementNumbers.h" #undef ELEMENT_NUMBERS_DECLARE }; - -#endif diff --git a/src/simulation/ElementClasses.h b/src/simulation/ElementClasses.h index 329b894a0..eae598970 100644 --- a/src/simulation/ElementClasses.h +++ b/src/simulation/ElementClasses.h @@ -1,6 +1,4 @@ -#ifndef ELEMENTCLASSES_H -#define ELEMENTCLASSES_H - +#pragma once #include #include "SimulationData.h" @@ -11,5 +9,3 @@ #undef ELEMENT_NUMBERS_ENUMERATE std::vector const &GetElements(); - -#endif diff --git a/src/simulation/ElementCommon.h b/src/simulation/ElementCommon.h index e10b770a1..28bc54963 100644 --- a/src/simulation/ElementCommon.h +++ b/src/simulation/ElementCommon.h @@ -1,35 +1,24 @@ -#ifndef ELEMENTCOMMON_H -#define ELEMENTCOMMON_H - -#include "Config.h" - +#pragma once // This header should be included by all files in src/elements/ - -#include - #include "Misc.h" - #include "common/tpt-rand.h" #include "common/tpt-compat.h" #include "common/tpt-minmax.h" - #include "ElementDefs.h" #include "ElementClasses.h" #include "Particle.h" #include "ElementGraphics.h" #include "Simulation.h" - #include "graphics/Renderer.h" +#include -#define IPL -257.0f -#define IPH 257.0f -#define ITL MIN_TEMP-1 -#define ITH MAX_TEMP+1 +constexpr float IPL = MIN_PRESSURE - 1; +constexpr float IPH = MAX_PRESSURE + 1; +constexpr float ITL = MIN_TEMP - 1; +constexpr float ITH = MAX_TEMP + 1; // no transition (PT_NONE means kill part) -#define NT -1 +constexpr int NT = -1; // special transition - lava ctypes etc need extra code, which is only found and run if ST is given -#define ST PT_NUM - -#endif +constexpr int ST = PT_NUM; diff --git a/src/simulation/ElementDefs.h b/src/simulation/ElementDefs.h index 1760dcafd..b134611fd 100644 --- a/src/simulation/ElementDefs.h +++ b/src/simulation/ElementDefs.h @@ -1,44 +1,42 @@ -#ifndef ELEMENTS_H_ -#define ELEMENTS_H_ +#pragma once +#include "SimulationConfig.h" +#include -#include "Config.h" -//#include "Simulation.h" +constexpr float MAX_TEMP = 9999; +constexpr float MIN_TEMP = 0; +constexpr float O_MAX_TEMP = 3500; +constexpr float O_MIN_TEMP = -273; -#define MAX_TEMP 9999 -#define MIN_TEMP 0 -#define O_MAX_TEMP 3500 -#define O_MIN_TEMP -273 +constexpr float MAX_PRESSURE = 256.0f; +constexpr float MIN_PRESSURE = -256.0f; -#define MAX_PRESSURE 256.0f -#define MIN_PRESSURE -256.0f +constexpr auto TYPE_PART = UINT32_C(0x00000001); //1 Powders +constexpr auto TYPE_LIQUID = UINT32_C(0x00000002); //2 Liquids +constexpr auto TYPE_SOLID = UINT32_C(0x00000004); //4 Solids +constexpr auto TYPE_GAS = UINT32_C(0x00000008); //8 Gases (Includes plasma) +constexpr auto TYPE_ENERGY = UINT32_C(0x00000010); //16 Energy (Thunder, Light, Neutrons etc.) +constexpr auto STATE_FLAGS = UINT32_C(0x0000001F); +constexpr auto PROP_CONDUCTS = UINT32_C(0x00000020); //32 Conducts electricity +constexpr auto PROP_BLACK = UINT32_C(0x00000040); //64 Absorbs Photons (not currently implemented or used, a photwl attribute might be better) +constexpr auto PROP_NEUTPENETRATE = UINT32_C(0x00000080); //128 Penetrated by neutrons +constexpr auto PROP_NEUTABSORB = UINT32_C(0x00000100); //256 Absorbs neutrons, reflect is default +constexpr auto PROP_NEUTPASS = UINT32_C(0x00000200); //512 Neutrons pass through, such as with glass +constexpr auto PROP_DEADLY = UINT32_C(0x00000400); //1024 Is deadly for stickman +constexpr auto PROP_HOT_GLOW = UINT32_C(0x00000800); //2048 Hot Metal Glow +constexpr auto PROP_LIFE = UINT32_C(0x00001000); //4096 Is a GoL type +constexpr auto PROP_RADIOACTIVE = UINT32_C(0x00002000); //8192 Radioactive +constexpr auto PROP_LIFE_DEC = UINT32_C(0x00004000); //2^14 Life decreases by one every frame if > zero +constexpr auto PROP_LIFE_KILL = UINT32_C(0x00008000); //2^15 Kill when life value is <= zero +constexpr auto PROP_LIFE_KILL_DEC = UINT32_C(0x00010000); //2^16 Kill when life value is decremented to <= zero +constexpr auto PROP_SPARKSETTLE = UINT32_C(0x00020000); //2^17 Allow Sparks/Embers to settle +constexpr auto PROP_NOAMBHEAT = UINT32_C(0x00040000); //2^18 Don't transfer or receive heat from ambient heat. +constexpr auto PROP_NOCTYPEDRAW = UINT32_C(0x00100000); // 2^20 When this element is drawn upon with, do not set ctype (like BCLN for CLNE) -#define TYPE_PART 0x00001 //1 Powders -#define TYPE_LIQUID 0x00002 //2 Liquids -#define TYPE_SOLID 0x00004 //4 Solids -#define TYPE_GAS 0x00008 //8 Gases (Includes plasma) -#define TYPE_ENERGY 0x00010 //16 Energy (Thunder, Light, Neutrons etc.) -#define STATE_FLAGS 0x0001F -#define PROP_CONDUCTS 0x00020 //32 Conducts electricity -#define PROP_BLACK 0x00040 //64 Absorbs Photons (not currently implemented or used, a photwl attribute might be better) -#define PROP_NEUTPENETRATE 0x00080 //128 Penetrated by neutrons -#define PROP_NEUTABSORB 0x00100 //256 Absorbs neutrons, reflect is default -#define PROP_NEUTPASS 0x00200 //512 Neutrons pass through, such as with glass -#define PROP_DEADLY 0x00400 //1024 Is deadly for stickman -#define PROP_HOT_GLOW 0x00800 //2048 Hot Metal Glow -#define PROP_LIFE 0x01000 //4096 Is a GoL type -#define PROP_RADIOACTIVE 0x02000 //8192 Radioactive -#define PROP_LIFE_DEC 0x04000 //2^14 Life decreases by one every frame if > zero -#define PROP_LIFE_KILL 0x08000 //2^15 Kill when life value is <= zero -#define PROP_LIFE_KILL_DEC 0x10000 //2^16 Kill when life value is decremented to <= zero -#define PROP_SPARKSETTLE 0x20000 //2^17 Allow Sparks/Embers to settle -#define PROP_NOAMBHEAT 0x40000 //2^18 Don't transfer or receive heat from ambient heat. -#define PROP_NOCTYPEDRAW 0x100000 // 2^20 When this element is drawn upon with, do not set ctype (like BCLN for CLNE) - -#define FLAG_STAGNANT 0x1 -#define FLAG_SKIPMOVE 0x2 // skip movement for one frame, only implemented for PHOT +constexpr auto FLAG_STAGNANT = UINT32_C(0x00000001); +constexpr auto FLAG_SKIPMOVE = UINT32_C(0x00000002); // skip movement for one frame, only implemented for PHOT //#define FLAG_WATEREQUAL 0x4 //if a liquid was already checked during equalization -#define FLAG_MOVABLE 0x8 // compatibility with old saves (moving SPNG), only applies to SPNG -#define FLAG_PHOTDECO 0x8 // compatibility with old saves (decorated photons), only applies to PHOT. Having the same value as FLAG_MOVABLE is fine because they apply to different elements, and this saves space for future flags, +constexpr auto FLAG_MOVABLE = UINT32_C(0x00000008); // compatibility with old saves (moving SPNG), only applies to SPNG +constexpr auto FLAG_PHOTDECO = UINT32_C(0x00000008); // compatibility with old saves (decorated photons), only applies to PHOT. Having the same value as FLAG_MOVABLE is fine because they apply to different elements, and this saves space for future flags, #define UPDATE_FUNC_ARGS Simulation* sim, int i, int x, int y, int surround_space, int nt, Particle *parts, int pmap[YRES][XRES] @@ -56,21 +54,29 @@ #define CTYPEDRAW_FUNC_ARGS Simulation *sim, int i, int t, int v #define CTYPEDRAW_FUNC_SUBCALL_ARGS sim, i, t, v -#define BOUNDS_CHECK true +constexpr bool BOUNDS_CHECK = true; -#define OLD_PT_WIND 147 +constexpr int OLD_PT_WIND = 147; // Change this to change the amount of bits used to store type in pmap (and a few elements such as PIPE and CRAY) -#define PMAPBITS 9 -#define PMAPMASK ((1<>PMAPBITS) -#define TYP(r) ((r)&PMAPMASK) -#define PMAP(id, typ) ((id)<> PMAPBITS; +} +constexpr int TYP(int r) +{ + return r & PMAPMASK; +} +constexpr int PMAP(int id, int typ) +{ + return (id << PMAPBITS) | (typ & PMAPMASK); +} +constexpr int PMAPID(int id) +{ + return id << PMAPBITS; +} +constexpr int PT_NUM = 1 << PMAPBITS; struct playerst; - - -#endif /* ELEMENTS_H_ */ diff --git a/src/simulation/ElementGraphics.h b/src/simulation/ElementGraphics.h index 1069d8d1d..1c48eeaa2 100644 --- a/src/simulation/ElementGraphics.h +++ b/src/simulation/ElementGraphics.h @@ -1,57 +1,55 @@ -#ifndef PGRAPHICS_H -#define PGRAPHICS_H +#pragma once +#include -#define PMODE 0x00000FFF -#define PMODE_NONE 0x00000000 -#define PMODE_FLAT 0x00000001 -#define PMODE_BLOB 0x00000002 -#define PMODE_BLUR 0x00000004 -#define PMODE_GLOW 0x00000008 -#define PMODE_SPARK 0x00000010 -#define PMODE_FLARE 0x00000020 -#define PMODE_LFLARE 0x00000040 -#define PMODE_ADD 0x00000080 -#define PMODE_BLEND 0x00000100 -#define PSPEC_STICKMAN 0x00000200 +constexpr auto PMODE = UINT32_C(0x00000FFF); +constexpr auto PMODE_NONE = UINT32_C(0x00000000); +constexpr auto PMODE_FLAT = UINT32_C(0x00000001); +constexpr auto PMODE_BLOB = UINT32_C(0x00000002); +constexpr auto PMODE_BLUR = UINT32_C(0x00000004); +constexpr auto PMODE_GLOW = UINT32_C(0x00000008); +constexpr auto PMODE_SPARK = UINT32_C(0x00000010); +constexpr auto PMODE_FLARE = UINT32_C(0x00000020); +constexpr auto PMODE_LFLARE = UINT32_C(0x00000040); +constexpr auto PMODE_ADD = UINT32_C(0x00000080); +constexpr auto PMODE_BLEND = UINT32_C(0x00000100); +constexpr auto PSPEC_STICKMAN = UINT32_C(0x00000200); -#define OPTIONS 0x0000F000 -#define NO_DECO 0x00001000 -#define DECO_FIRE 0x00002000 +constexpr auto OPTIONS = UINT32_C(0x0000F000); +constexpr auto NO_DECO = UINT32_C(0x00001000); +constexpr auto DECO_FIRE = UINT32_C(0x00002000); -#define FIREMODE 0x00FF0000 -#define FIRE_ADD 0x00010000 -#define FIRE_BLEND 0x00020000 -#define FIRE_SPARK 0x00040000 +constexpr auto FIREMODE = UINT32_C(0x00FF0000); +constexpr auto FIRE_ADD = UINT32_C(0x00010000); +constexpr auto FIRE_BLEND = UINT32_C(0x00020000); +constexpr auto FIRE_SPARK = UINT32_C(0x00040000); -#define EFFECT 0xFF000000 -#define EFFECT_GRAVIN 0x01000000 -#define EFFECT_GRAVOUT 0x02000000 -#define EFFECT_LINES 0x04000000 -#define EFFECT_DBGLINES 0x08000000 +constexpr auto EFFECT = UINT32_C(0xFF000000); +constexpr auto EFFECT_GRAVIN = UINT32_C(0x01000000); +constexpr auto EFFECT_GRAVOUT = UINT32_C(0x02000000); +constexpr auto EFFECT_LINES = UINT32_C(0x04000000); +constexpr auto EFFECT_DBGLINES = UINT32_C(0x08000000); -#define RENDER_EFFE OPTIONS | PSPEC_STICKMAN | EFFECT | PMODE_SPARK | PMODE_FLARE | PMODE_LFLARE -#define RENDER_FIRE OPTIONS | PSPEC_STICKMAN | /*PMODE_FLAT |*/ PMODE_ADD | PMODE_BLEND | FIRE_ADD | FIRE_BLEND -#define RENDER_SPRK OPTIONS | PSPEC_STICKMAN | PMODE_ADD | PMODE_BLEND | FIRE_SPARK -#define RENDER_GLOW OPTIONS | PSPEC_STICKMAN | /*PMODE_FLAT |*/ PMODE_GLOW | PMODE_ADD | PMODE_BLEND -#define RENDER_BLUR OPTIONS | PSPEC_STICKMAN | /*PMODE_FLAT |*/ PMODE_BLUR | PMODE_ADD | PMODE_BLEND -#define RENDER_BLOB OPTIONS | PSPEC_STICKMAN | /*PMODE_FLAT |*/ PMODE_BLOB | PMODE_ADD | PMODE_BLEND -#define RENDER_BASC OPTIONS | PSPEC_STICKMAN | PMODE_FLAT | PMODE_ADD | PMODE_BLEND | EFFECT_LINES -#define RENDER_NONE OPTIONS | PSPEC_STICKMAN | PMODE_FLAT +constexpr auto RENDER_EFFE = OPTIONS | PSPEC_STICKMAN | EFFECT | PMODE_SPARK | PMODE_FLARE | PMODE_LFLARE; +constexpr auto RENDER_FIRE = OPTIONS | PSPEC_STICKMAN | PMODE_ADD | PMODE_BLEND | FIRE_ADD | FIRE_BLEND; +constexpr auto RENDER_SPRK = OPTIONS | PSPEC_STICKMAN | PMODE_ADD | PMODE_BLEND | FIRE_SPARK; +constexpr auto RENDER_GLOW = OPTIONS | PSPEC_STICKMAN | PMODE_GLOW | PMODE_ADD | PMODE_BLEND; +constexpr auto RENDER_BLUR = OPTIONS | PSPEC_STICKMAN | PMODE_BLUR | PMODE_ADD | PMODE_BLEND; +constexpr auto RENDER_BLOB = OPTIONS | PSPEC_STICKMAN | PMODE_BLOB | PMODE_ADD | PMODE_BLEND; +constexpr auto RENDER_BASC = OPTIONS | PSPEC_STICKMAN | PMODE_FLAT | PMODE_ADD | PMODE_BLEND | EFFECT_LINES; +constexpr auto RENDER_NONE = OPTIONS | PSPEC_STICKMAN | PMODE_FLAT; -#define COLOUR_HEAT 0x00000001 -#define COLOUR_LIFE 0x00000002 -#define COLOUR_GRAD 0x00000004 -#define COLOUR_BASC 0x00000008 +constexpr auto COLOUR_HEAT = UINT32_C(0x00000001); +constexpr auto COLOUR_LIFE = UINT32_C(0x00000002); +constexpr auto COLOUR_GRAD = UINT32_C(0x00000004); +constexpr auto COLOUR_BASC = UINT32_C(0x00000008); -#define COLOUR_DEFAULT 0x00000000 +constexpr auto COLOUR_DEFAULT = UINT32_C(0x00000000); -#define DISPLAY_AIRC 0x00000001 -#define DISPLAY_AIRP 0x00000002 -#define DISPLAY_AIRV 0x00000004 -#define DISPLAY_AIRH 0x00000008 -#define DISPLAY_AIR 0x0000000F -#define DISPLAY_WARP 0x00000010 -#define DISPLAY_PERS 0x00000020 -#define DISPLAY_EFFE 0x00000040 - -#endif +constexpr auto DISPLAY_AIRC = UINT32_C(0x00000001); +constexpr auto DISPLAY_AIRP = UINT32_C(0x00000002); +constexpr auto DISPLAY_AIRV = UINT32_C(0x00000004); +constexpr auto DISPLAY_AIRH = UINT32_C(0x00000008); +constexpr auto DISPLAY_AIR = UINT32_C(0x0000000F); +constexpr auto DISPLAY_WARP = UINT32_C(0x00000010); +constexpr auto DISPLAY_PERS = UINT32_C(0x00000020); +constexpr auto DISPLAY_EFFE = UINT32_C(0x00000040); diff --git a/src/simulation/GOLString.cpp b/src/simulation/GOLString.cpp index 9394efacc..859bd0ed4 100644 --- a/src/simulation/GOLString.cpp +++ b/src/simulation/GOLString.cpp @@ -1,5 +1,4 @@ #include "GOLString.h" -#include "client/Client.h" int ParseGOLString(const String &value) { @@ -94,32 +93,3 @@ String SerialiseGOLRule(int rule) } return golName.Build(); } - -#ifndef RENDERER -bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor) -{ - auto customGOLTypes = Client::Ref().GetPrefByteStringArray("CustomGOL.Types"); - Json::Value newCustomGOLTypes(Json::arrayValue); - bool nameTaken = false; - for (auto gol : customGOLTypes) - { - auto parts = gol.FromUtf8().PartitionBy(' '); - if (parts.size()) - { - if (parts[0] == nameString) - { - nameTaken = true; - } - } - newCustomGOLTypes.append(gol); - } - if (nameTaken) - return false; - - StringBuilder sb; - sb << nameString << " " << ruleString << " " << highColor << " " << lowColor; - newCustomGOLTypes.append(sb.Build().ToUtf8()); - Client::Ref().SetPref("CustomGOL.Types", newCustomGOLTypes); - return true; -} -#endif diff --git a/src/simulation/GOLString.h b/src/simulation/GOLString.h index 766a1dc1b..78799594b 100644 --- a/src/simulation/GOLString.h +++ b/src/simulation/GOLString.h @@ -6,4 +6,3 @@ bool ValidateGOLName(const String &value); int ParseGOLString(const String &value); String SerialiseGOLRule(int rule); -bool AddCustomGol(String ruleString, String nameString, unsigned int highColor, unsigned int lowColor); diff --git a/src/simulation/Gravity.cpp b/src/simulation/Gravity.cpp deleted file mode 100755 index 171980a02..000000000 --- a/src/simulation/Gravity.cpp +++ /dev/null @@ -1,526 +0,0 @@ -#include "Gravity.h" - -#include -#include -#include - -#include "CoordStack.h" -#include "Misc.h" -#include "Simulation.h" -#include "SimulationData.h" - - -Gravity::Gravity() -{ - // Allocate full size Gravmaps - unsigned int size = (XRES / CELL) * (YRES / CELL); - th_ogravmap = new float[size]; - th_gravmap = new float[size]; - th_gravy = new float[size]; - th_gravx = new float[size]; - th_gravp = new float[size]; - gravmap = new float[size]; - gravy = new float[size]; - gravx = new float[size]; - gravp = new float[size]; - gravmask = new unsigned[size]; -} - -Gravity::~Gravity() -{ - stop_grav_async(); -#ifdef GRAVFFT - grav_fft_cleanup(); -#endif - - delete[] th_ogravmap; - delete[] th_gravmap; - delete[] th_gravy; - delete[] th_gravx; - delete[] th_gravp; - delete[] gravmap; - delete[] gravy; - delete[] gravx; - delete[] gravp; - delete[] gravmask; -} - -void Gravity::Clear() -{ - int size = (XRES / CELL) * (YRES / CELL); - std::fill(gravy, gravy + size, 0.0f); - std::fill(gravx, gravx + size, 0.0f); - std::fill(gravp, gravp + size, 0.0f); - std::fill(gravmap, gravmap + size, 0.0f); - std::fill(gravmask, gravmask + size, 0xFFFFFFFF); - - ignoreNextResult = true; -} - -#ifdef GRAVFFT -void Gravity::grav_fft_init() -{ - int xblock2 = XRES/CELL*2; - int yblock2 = YRES/CELL*2; - int fft_tsize = (xblock2/2+1)*yblock2; - float distance, scaleFactor; - fftwf_plan plan_ptgravx, plan_ptgravy; - if (grav_fft_status) return; - - //use fftw malloc function to ensure arrays are aligned, to get better performance - th_ptgravx = reinterpret_cast(fftwf_malloc(xblock2 * yblock2 * sizeof(float))); - th_ptgravy = reinterpret_cast(fftwf_malloc(xblock2 * yblock2 * sizeof(float))); - th_ptgravxt = reinterpret_cast(fftwf_malloc(fft_tsize * sizeof(fftwf_complex))); - th_ptgravyt = reinterpret_cast(fftwf_malloc(fft_tsize * sizeof(fftwf_complex))); - th_gravmapbig = reinterpret_cast(fftwf_malloc(xblock2 * yblock2 * sizeof(float))); - th_gravmapbigt = reinterpret_cast(fftwf_malloc(fft_tsize * sizeof(fftwf_complex))); - th_gravxbig = reinterpret_cast(fftwf_malloc(xblock2 * yblock2 * sizeof(float))); - th_gravybig = reinterpret_cast(fftwf_malloc(xblock2 * yblock2 * sizeof(float))); - th_gravxbigt = reinterpret_cast(fftwf_malloc(fft_tsize * sizeof(fftwf_complex))); - th_gravybigt = reinterpret_cast(fftwf_malloc(fft_tsize * sizeof(fftwf_complex))); - - //select best algorithm, could use FFTW_PATIENT or FFTW_EXHAUSTIVE but that increases the time taken to plan, and I don't see much increase in execution speed - plan_ptgravx = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravx, th_ptgravxt, FFTW_MEASURE); - plan_ptgravy = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravy, th_ptgravyt, FFTW_MEASURE); - plan_gravmap = fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_gravmapbig, th_gravmapbigt, FFTW_MEASURE); - plan_gravx_inverse = fftwf_plan_dft_c2r_2d(yblock2, xblock2, th_gravxbigt, th_gravxbig, FFTW_MEASURE); - plan_gravy_inverse = fftwf_plan_dft_c2r_2d(yblock2, xblock2, th_gravybigt, th_gravybig, FFTW_MEASURE); - - //(XRES/CELL)*(YRES/CELL)*4 is size of data array, scaling needed because FFTW calculates an unnormalized DFT - scaleFactor = -float(M_GRAV)/((XRES/CELL)*(YRES/CELL)*4); - //calculate velocity map caused by a point mass - for (int y = 0; y < yblock2; y++) - { - for (int x = 0; x < xblock2; x++) - { - if (x == XRES / CELL && y == YRES / CELL) - continue; - distance = sqrtf(pow(x-(XRES/CELL), 2.0f) + pow(y-(YRES/CELL), 2.0f)); - th_ptgravx[y * xblock2 + x] = scaleFactor * (x - (XRES / CELL)) / pow(distance, 3); - th_ptgravy[y * xblock2 + x] = scaleFactor * (y - (YRES / CELL)) / pow(distance, 3); - } - } - th_ptgravx[yblock2 * xblock2 / 2 + xblock2 / 2] = 0.0f; - th_ptgravy[yblock2 * xblock2 / 2 + xblock2 / 2] = 0.0f; - - //transform point mass velocity maps - fftwf_execute(plan_ptgravx); - fftwf_execute(plan_ptgravy); - fftwf_destroy_plan(plan_ptgravx); - fftwf_destroy_plan(plan_ptgravy); - fftwf_free(th_ptgravx); - fftwf_free(th_ptgravy); - - //clear padded gravmap - memset(th_gravmapbig, 0, xblock2 * yblock2 * sizeof(float)); - - grav_fft_status = true; -} - -void Gravity::grav_fft_cleanup() -{ - if (!grav_fft_status) return; - fftwf_free(th_ptgravxt); - fftwf_free(th_ptgravyt); - fftwf_free(th_gravmapbig); - fftwf_free(th_gravmapbigt); - fftwf_free(th_gravxbig); - fftwf_free(th_gravybig); - fftwf_free(th_gravxbigt); - fftwf_free(th_gravybigt); - fftwf_destroy_plan(plan_gravmap); - fftwf_destroy_plan(plan_gravx_inverse); - fftwf_destroy_plan(plan_gravy_inverse); - grav_fft_status = false; -} -#endif - -void Gravity::gravity_update_async() -{ - int result; - if (!enabled) - return; - - bool signal_grav = false; - - { - std::unique_lock l(gravmutex, std::defer_lock); - if (l.try_lock()) - { - result = grav_ready; - if (result) //Did the gravity thread finish? - { - if (th_gravchanged && !ignoreNextResult) - { -#if !defined(GRAVFFT) && defined(GRAV_DIFF) - memcpy(gravy, th_gravy, (XRES/CELL)*(YRES/CELL)*sizeof(float)); - memcpy(gravx, th_gravx, (XRES/CELL)*(YRES/CELL)*sizeof(float)); - memcpy(gravp, th_gravp, (XRES/CELL)*(YRES/CELL)*sizeof(float)); -#else - // Copy thread gravity maps into this one - std::swap(gravy, th_gravy); - std::swap(gravx, th_gravx); - std::swap(gravp, th_gravp); -#endif - } - ignoreNextResult = false; - - std::swap(gravmap, th_gravmap); - - grav_ready = 0; //Tell the other thread that we're ready for it to continue - signal_grav = true; - } - } - } - - if (signal_grav) - { - gravcv.notify_one(); - } - unsigned int size = (XRES / CELL) * (YRES / CELL); - membwand(gravy, gravmask, size * sizeof(float), size * sizeof(unsigned)); - membwand(gravx, gravmask, size * sizeof(float), size * sizeof(unsigned)); - std::fill(&gravmap[0], &gravmap[size], 0.0f); -} - -void Gravity::update_grav_async() -{ - int done = 0; - int thread_done = 0; - unsigned int size = (XRES / CELL) * (YRES / CELL); - std::fill(&th_ogravmap[0], &th_ogravmap[size], 0.0f); - std::fill(&th_gravmap[0], &th_gravmap[size], 0.0f); - std::fill(&th_gravy[0], &th_gravy[size], 0.0f); - std::fill(&th_gravx[0], &th_gravx[size], 0.0f); - std::fill(&th_gravp[0], &th_gravp[size], 0.0f); - -#ifdef GRAVFFT - if (!grav_fft_status) - grav_fft_init(); -#endif - - std::unique_lock l(gravmutex); - while (!thread_done) - { - if (!done) - { - // run gravity update - update_grav(); - done = 1; - grav_ready = 1; - thread_done = gravthread_done; - } - else - { - // wait for main thread - gravcv.wait(l); - done = grav_ready; - thread_done = gravthread_done; - } - } -} - -void Gravity::start_grav_async() -{ - if (enabled) //If it's already enabled, restart it - stop_grav_async(); - - gravthread_done = 0; - grav_ready = 0; - gravthread = std::thread([this]() { update_grav_async(); }); //Start asynchronous gravity simulation - enabled = true; - - unsigned int size = (XRES / CELL) * (YRES / CELL); - std::fill(&gravy[0], &gravy[size], 0.0f); - std::fill(&gravx[0], &gravx[size], 0.0f); - std::fill(&gravp[0], &gravp[size], 0.0f); - std::fill(&gravmap[0], &gravmap[size], 0.0f); -} - -void Gravity::stop_grav_async() -{ - if (enabled) - { - { - std::lock_guard g(gravmutex); - gravthread_done = 1; - } - gravcv.notify_one(); - gravthread.join(); - enabled = false; - } - // Clear the grav velocities - unsigned int size = (XRES / CELL) * (YRES / CELL); - std::fill(&gravy[0], &gravy[size], 0.0f); - std::fill(&gravx[0], &gravx[size], 0.0f); - std::fill(&gravp[0], &gravp[size], 0.0f); - std::fill(&gravmap[0], &gravmap[size], 0.0f); -} - -#ifdef GRAVFFT -void Gravity::update_grav() -{ - int xblock2 = XRES/CELL*2, yblock2 = YRES/CELL*2; - int fft_tsize = (xblock2/2+1)*yblock2; - float mr, mc, pr, pc, gr, gc; - if (memcmp(th_ogravmap, th_gravmap, sizeof(float)*(XRES/CELL)*(YRES/CELL)) != 0) - { - th_gravchanged = 1; - - membwand(th_gravmap, gravmask, (XRES/CELL)*(YRES/CELL)*sizeof(float), (XRES/CELL)*(YRES/CELL)*sizeof(unsigned)); - //copy gravmap into padded gravmap array - for (int y = 0; y < YRES / CELL; y++) - { - for (int x = 0; x < XRES / CELL; x++) - { - th_gravmapbig[(y+YRES/CELL)*xblock2+XRES/CELL+x] = th_gravmap[y*(XRES/CELL)+x]; - } - } - //transform gravmap - fftwf_execute(plan_gravmap); - //do convolution (multiply the complex numbers) - for (int i = 0; i < fft_tsize; i++) - { - mr = th_gravmapbigt[i][0]; - mc = th_gravmapbigt[i][1]; - pr = th_ptgravxt[i][0]; - pc = th_ptgravxt[i][1]; - gr = mr*pr-mc*pc; - gc = mr*pc+mc*pr; - th_gravxbigt[i][0] = gr; - th_gravxbigt[i][1] = gc; - pr = th_ptgravyt[i][0]; - pc = th_ptgravyt[i][1]; - gr = mr*pr-mc*pc; - gc = mr*pc+mc*pr; - th_gravybigt[i][0] = gr; - th_gravybigt[i][1] = gc; - } - //inverse transform, and copy from padded arrays into normal velocity maps - fftwf_execute(plan_gravx_inverse); - fftwf_execute(plan_gravy_inverse); - for (int y = 0; y < YRES / CELL; y++) - { - for (int x = 0; x < XRES / CELL; x++) - { - th_gravx[y*(XRES/CELL)+x] = th_gravxbig[y*xblock2+x]; - th_gravy[y*(XRES/CELL)+x] = th_gravybig[y*xblock2+x]; - th_gravp[y*(XRES/CELL)+x] = sqrtf(pow(th_gravxbig[y*xblock2+x],2)+pow(th_gravybig[y*xblock2+x],2)); - } - } - } - else - { - th_gravchanged = 0; - } - - // Copy th_ogravmap into th_gravmap (doesn't matter what th_ogravmap is afterwards) - std::swap(th_gravmap, th_ogravmap); -} - -#else -// gravity without fast Fourier transforms - -void Gravity::update_grav(void) -{ - int x, y, i, j; - float val, distance; - th_gravchanged = 0; -#ifndef GRAV_DIFF - //Find any changed cells - for (i=0; i 0.0001f || th_gravmap[i*(XRES/CELL)+j]<-0.0001f) //Only calculate with populated or changed cells. - { -#endif - for (y = 0; y < YRES / CELL; y++) { - for (x = 0; x < XRES / CELL; x++) { - if (x == j && y == i)//Ensure it doesn't calculate with itself - continue; - distance = sqrt(pow(j - x, 2.0f) + pow(i - y, 2.0f)); -#ifdef GRAV_DIFF - val = th_gravmap[i*(XRES/CELL)+j] - th_ogravmap[i*(XRES/CELL)+j]; -#else - val = th_gravmap[i*(XRES/CELL)+j]; -#endif - th_gravx[y*(XRES/CELL)+x] += M_GRAV * val * (j - x) / pow(distance, 3.0f); - th_gravy[y*(XRES/CELL)+x] += M_GRAV * val * (i - y) / pow(distance, 3.0f); - th_gravp[y*(XRES/CELL)+x] += M_GRAV * val / pow(distance, 2.0f); - } - } - } - } - } - memcpy(th_ogravmap, th_gravmap, (XRES/CELL)*(YRES/CELL)*sizeof(float)); -} -#endif - - - -bool Gravity::grav_mask_r(int x, int y, char checkmap[YRES/CELL][XRES/CELL], char shape[YRES/CELL][XRES/CELL]) -{ - int x1, x2; - bool ret = false; - try - { - CoordStack cs; - cs.push(x, y); - do - { - cs.pop(x, y); - x1 = x2 = x; - while (x1 >= 0) - { - if (x1 == 0) - { - ret = true; - break; - } - else if (checkmap[y][x1-1] || bmap[y][x1-1] == WL_GRAV) - break; - x1--; - } - while (x2 <= XRES/CELL-1) - { - if (x2 == XRES/CELL-1) - { - ret = true; - break; - } - else if (checkmap[y][x2+1] || bmap[y][x2+1] == WL_GRAV) - break; - x2++; - } - for (x = x1; x <= x2; x++) - { - shape[y][x] = 1; - checkmap[y][x] = 1; - } - if (y == 0) - { - for (x = x1; x <= x2; x++) - if (bmap[y][x] != WL_GRAV) - ret = true; - } - else if (y >= 1) - { - for (x = x1; x <= x2; x++) - if (!checkmap[y-1][x] && bmap[y-1][x] != WL_GRAV) - { - if (y-1 == 0) - ret = true; - cs.push(x, y-1); - } - } - if (y < YRES/CELL-1) - for (x=x1; x<=x2; x++) - if (!checkmap[y+1][x] && bmap[y+1][x] != WL_GRAV) - { - if (y+1 == YRES/CELL-1) - ret = true; - cs.push(x, y+1); - } - } while (cs.getSize()>0); - } - catch (std::exception& e) - { - std::cerr << e.what() << std::endl; - ret = false; - } - return ret; -} -void Gravity::mask_free(mask_el *c_mask_el) -{ - if (c_mask_el == nullptr) - return; - delete[] c_mask_el->next; - delete[] c_mask_el->shape; - delete[] c_mask_el; -} - -void Gravity::gravity_mask() -{ - char checkmap[YRES/CELL][XRES/CELL]; - unsigned maskvalue; - mask_el *t_mask_el = nullptr; - mask_el *c_mask_el = nullptr; - if (!gravmask) - return; - memset(checkmap, 0, sizeof(checkmap)); - for (int x = 0; x < XRES / CELL; x++) - { - for(int y = 0; y < YRES / CELL; y++) - { - if (bmap[y][x] != WL_GRAV && checkmap[y][x] == 0) - { - // Create a new shape - if (t_mask_el == nullptr) - { - t_mask_el = new mask_el[sizeof(mask_el)]; - t_mask_el->shape = new char[(XRES / CELL) * (YRES / CELL)]; - std::fill(&t_mask_el->shape[0], &t_mask_el->shape[(XRES / CELL) * (YRES / CELL)], 0); - t_mask_el->shapeout = 0; - t_mask_el->next = nullptr; - c_mask_el = t_mask_el; - } - else - { - c_mask_el->next = new mask_el[sizeof(mask_el)]; - c_mask_el = c_mask_el->next; - c_mask_el->shape = new char[(XRES / CELL) * (YRES / CELL)]; - std::fill(&c_mask_el->shape[0], &c_mask_el->shape[(XRES / CELL) * (YRES / CELL)], 0); - c_mask_el->shapeout = 0; - c_mask_el->next = nullptr; - } - // Fill the shape - if (grav_mask_r(x, y, checkmap, reinterpret_cast(c_mask_el->shape))) - c_mask_el->shapeout = 1; - } - } - } - c_mask_el = t_mask_el; - std::fill(&gravmask[0], &gravmask[(XRES / CELL) * (YRES / CELL)], 0); - while (c_mask_el != nullptr) - { - char *cshape = c_mask_el->shape; - for (int x = 0; x < XRES / CELL; x++) - { - for (int y = 0; y < YRES / CELL; y++) - { - if (cshape[y * (XRES / CELL) + x]) - { - if (c_mask_el->shapeout) - maskvalue = 0xFFFFFFFF; - else - maskvalue = 0x00000000; - gravmask[y * (XRES / CELL) + x] = maskvalue; - } - } - } - c_mask_el = c_mask_el->next; - } - mask_free(t_mask_el); -} diff --git a/src/simulation/Gravity.h b/src/simulation/Gravity.h deleted file mode 100644 index ec3b83e4c..000000000 --- a/src/simulation/Gravity.h +++ /dev/null @@ -1,92 +0,0 @@ -#ifndef GRAVITY_H -#define GRAVITY_H -#include "Config.h" - -#include -#include -#include - -#ifdef GRAVFFT -#include -#endif - -class Simulation; - -class Gravity -{ -private: - - bool enabled = false; - - // Maps to be processed by the gravity thread - float *th_ogravmap = nullptr; - float *th_gravmap = nullptr; - float *th_gravx = nullptr; - float *th_gravy = nullptr; - float *th_gravp = nullptr; - - int th_gravchanged = 0; - - std::thread gravthread; - std::mutex gravmutex; - std::condition_variable gravcv; - int grav_ready = 0; - int gravthread_done = 0; - bool ignoreNextResult = false; - -#ifdef GRAVFFT - bool grav_fft_status = false; - float *th_ptgravx = nullptr; - float *th_ptgravy = nullptr; - float *th_gravmapbig = nullptr; - float *th_gravxbig = nullptr; - float *th_gravybig = nullptr; - - fftwf_complex *th_ptgravxt, *th_ptgravyt, *th_gravmapbigt, *th_gravxbigt, *th_gravybigt; - fftwf_plan plan_gravmap, plan_gravx_inverse, plan_gravy_inverse; -#endif - - struct mask_el { - char *shape; - char shapeout; - mask_el *next; - }; - using mask_el = struct mask_el; - - bool grav_mask_r(int x, int y, char checkmap[YRES/CELL][XRES/CELL], char shape[YRES/CELL][XRES/CELL]); - void mask_free(mask_el *c_mask_el); - - void update_grav(); - void update_grav_async(); - - -#ifdef GRAVFFT - void grav_fft_init(); - void grav_fft_cleanup(); -#endif - -public: - //Maps to be used by the main thread - float *gravmap = nullptr; - float *gravp = nullptr; - float *gravy = nullptr; - float *gravx = nullptr; - unsigned *gravmask = nullptr; - - unsigned char (*bmap)[XRES/CELL]; - - bool IsEnabled() { return enabled; } - - void Clear(); - - void gravity_update_async(); - - void start_grav_async(); - void stop_grav_async(); - void gravity_mask(); - - Gravity(); - ~Gravity(); -}; - -#endif diff --git a/src/simulation/MenuSection.h b/src/simulation/MenuSection.h index 23be2b53b..03bef9844 100644 --- a/src/simulation/MenuSection.h +++ b/src/simulation/MenuSection.h @@ -1,5 +1,5 @@ -#ifndef MENUSECTION_H_ -#define MENUSECTION_H_ +#pragma once +#include "common/String.h" struct menu_section { @@ -8,5 +8,3 @@ struct menu_section int itemcount; int doshow; }; - -#endif diff --git a/src/simulation/NoToolClasses.cpp b/src/simulation/NoToolClasses.cpp new file mode 100644 index 000000000..8b591af3f --- /dev/null +++ b/src/simulation/NoToolClasses.cpp @@ -0,0 +1,7 @@ +#include "ToolClasses.h" + +std::vector const &GetTools() +{ + static std::vector tools; + return tools; +} diff --git a/src/simulation/Particle.cpp b/src/simulation/Particle.cpp index c9bc46ee8..8963b9e75 100644 --- a/src/simulation/Particle.cpp +++ b/src/simulation/Particle.cpp @@ -1,5 +1,6 @@ -#include #include "Particle.h" +#include +#include std::vector const &Particle::GetProperties() { @@ -31,3 +32,31 @@ std::vector const &Particle::GetPropertyAliases() }; return aliases; } + +std::vector const &Particle::PossiblyCarriesType() +{ + struct DoOnce + { + std::vector indices = { + FIELD_LIFE, + FIELD_CTYPE, + FIELD_TMP, + FIELD_TMP2, + FIELD_TMP3, + FIELD_TMP4, + }; + + DoOnce() + { + auto &properties = GetProperties(); + for (auto index : indices) + { + // code that depends on PossiblyCarriesType only knows how to set ints + assert(properties[index].Type == StructProperty::Integer || + properties[index].Type == StructProperty::ParticleType); + } + } + }; + static DoOnce doOnce; + return doOnce.indices; +} diff --git a/src/simulation/Particle.h b/src/simulation/Particle.h index 8a327838c..3114093ba 100644 --- a/src/simulation/Particle.h +++ b/src/simulation/Particle.h @@ -1,9 +1,6 @@ -#ifndef PARTICLE_H_ -#define PARTICLE_H_ -#include "Config.h" - -#include +#pragma once #include "StructProperty.h" +#include struct Particle { @@ -21,6 +18,13 @@ struct Particle by higher-level processes referring to them by name such as Lua or the property tool **/ static std::vector const &GetProperties(); static std::vector const &GetPropertyAliases(); + static std::vector const &PossiblyCarriesType(); }; -#endif +// important: these are indices into the vector returned by Particle::GetProperties, not indices into Particle +constexpr unsigned int FIELD_LIFE = 1; +constexpr unsigned int FIELD_CTYPE = 2; +constexpr unsigned int FIELD_TMP = 9; +constexpr unsigned int FIELD_TMP2 = 10; +constexpr unsigned int FIELD_TMP3 = 11; +constexpr unsigned int FIELD_TMP4 = 12; diff --git a/src/simulation/Sample.h b/src/simulation/Sample.h index d14344727..633a30fc3 100644 --- a/src/simulation/Sample.h +++ b/src/simulation/Sample.h @@ -1,6 +1,4 @@ -#ifndef The_Powder_Toy_Sample_h -#define The_Powder_Toy_Sample_h - +#pragma once #include "Particle.h" class SimulationSample @@ -24,5 +22,3 @@ public: SimulationSample() : particle(), ParticleID(0), PositionX(0), PositionY(0), AirPressure(0), AirTemperature(0), AirVelocityX(0), AirVelocityY(0), WallType(0), Gravity(0), GravityVelocityX(0), GravityVelocityY(0), NumParts(0), isMouseInSim(true) {} }; - -#endif diff --git a/src/simulation/SaveRenderer.cpp b/src/simulation/SaveRenderer.cpp index 48db5cde7..50b0dbd2a 100644 --- a/src/simulation/SaveRenderer.cpp +++ b/src/simulation/SaveRenderer.cpp @@ -8,9 +8,8 @@ #include "Simulation.h" SaveRenderer::SaveRenderer(){ - g = new Graphics(); sim = new Simulation(); - ren = new Renderer(g, sim); + ren = new Renderer(sim); ren->decorations_enable = true; ren->blackDecorations = true; } @@ -21,7 +20,7 @@ void SaveRenderer::Flush(int begin, int end) std::fill(ren->graphicscache + begin, ren->graphicscache + end, gcache_item()); } -VideoBuffer * SaveRenderer::Render(GameSave * save, bool decorations, bool fire, Renderer *renderModeSource) +std::unique_ptr SaveRenderer::Render(const GameSave *save, bool decorations, bool fire, Renderer *renderModeSource) { std::lock_guard gx(renderMutex); @@ -33,55 +32,39 @@ VideoBuffer * SaveRenderer::Render(GameSave * save, bool decorations, bool fire, ren->SetColourMode(renderModeSource->GetColourMode()); } - int width, height; - VideoBuffer * tempThumb = NULL; - width = save->blockWidth; - height = save->blockHeight; + std::unique_ptr tempThumb; - g->Clear(); sim->clear_sim(); - if(!sim->Load(save, true)) + sim->Load(save, true, { 0, 0 }); + ren->decorations_enable = true; + ren->blackDecorations = !decorations; + ren->ClearAccumulation(); + ren->clearScreen(); + + if (fire) { - ren->decorations_enable = true; - ren->blackDecorations = !decorations; - pixel * pData = NULL; - pixel * dst; - pixel * src = g->vid; - - ren->ClearAccumulation(); - - if (fire) + int frame = 15; + while(frame) { - int frame = 15; - while(frame) - { - frame--; - ren->render_parts(); - ren->render_fire(); - ren->clearScreen(1.0f); - } + frame--; + ren->render_parts(); + ren->render_fire(); + ren->clearScreen(); } - - ren->RenderBegin(); - ren->RenderEnd(); - - - pData = (pixel *)malloc(PIXELSIZE * ((width*CELL)*(height*CELL))); - dst = pData; - for(int i = 0; i < height*CELL; i++) - { - memcpy(dst, src, (width*CELL)*PIXELSIZE); - dst+=(width*CELL);///PIXELSIZE; - src+=WINDOWW; - } - tempThumb = new VideoBuffer(pData, width*CELL, height*CELL); - free(pData); } + ren->RenderBegin(); + ren->RenderEnd(); + + tempThumb = std::make_unique(save->blockSize * CELL); + tempThumb->BlendImage(ren->Data(), 0xFF, ren->Size().OriginRect()); + return tempThumb; } SaveRenderer::~SaveRenderer() { + delete ren; + delete sim; } diff --git a/src/simulation/SaveRenderer.h b/src/simulation/SaveRenderer.h index c0ca685bb..9dc3b6bcd 100644 --- a/src/simulation/SaveRenderer.h +++ b/src/simulation/SaveRenderer.h @@ -1,8 +1,7 @@ -#ifndef SAVERENDERER_H_ -#define SAVERENDERER_H_ -#include "Config.h" -#include "common/Singleton.h" +#pragma once +#include #include +#include "common/ExplicitSingleton.h" class GameSave; class VideoBuffer; @@ -10,17 +9,13 @@ class Graphics; class Simulation; class Renderer; -class SaveRenderer: public Singleton { - Graphics * g; +class SaveRenderer: public ExplicitSingleton { Simulation * sim; Renderer * ren; std::mutex renderMutex; public: SaveRenderer(); - VideoBuffer * Render(GameSave * save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr); - VideoBuffer * Render(unsigned char * saveData, int saveDataSize, bool decorations = true, bool fire = true); + std::unique_ptr Render(const GameSave *save, bool decorations = true, bool fire = true, Renderer *renderModeSource = nullptr); void Flush(int begin, int end); virtual ~SaveRenderer(); }; - -#endif /* SAVERENDERER_H_ */ diff --git a/src/simulation/Sign.cpp b/src/simulation/Sign.cpp index 735e2dd86..e885fcc65 100644 --- a/src/simulation/Sign.cpp +++ b/src/simulation/Sign.cpp @@ -134,7 +134,7 @@ String sign::getDisplayText(Simulation *sim, int &x0, int &y0, int &w, int &h, b } } - w = Graphics::textwidth(drawable_text.c_str()) + 5; + w = Graphics::TextSize(drawable_text.c_str()).X + 4; h = 15; x0 = (ju == Right) ? x - w : (ju == Left) ? x : x - w/2; y0 = (y > 18) ? y - 18 : y + 4; diff --git a/src/simulation/Sign.h b/src/simulation/Sign.h index a5ebfd157..211d46437 100644 --- a/src/simulation/Sign.h +++ b/src/simulation/Sign.h @@ -1,9 +1,5 @@ -#ifndef SIGN_H_ -#define SIGN_H_ -#include "Config.h" - +#pragma once #include "common/String.h" - #include class Simulation; @@ -35,5 +31,3 @@ struct sign String getDisplayText(Simulation *sim, int &x, int &y, int &w, int &h, bool colorize = true, bool *v95 = nullptr) const; std::pair split() const; }; - -#endif diff --git a/src/simulation/SimTool.cpp b/src/simulation/SimTool.cpp index dee52d81d..e25f579ba 100644 --- a/src/simulation/SimTool.cpp +++ b/src/simulation/SimTool.cpp @@ -1,18 +1,16 @@ -#include - #include "common/tpt-rand.h" #include "graphics/Renderer.h" #include "simulation/ElementGraphics.h" -#include "simulation/Gravity.h" +#include "simulation/gravity/Gravity.h" #include "simulation/Simulation.h" - #include "Misc.h" #include "ToolClasses.h" +#include SimTool::SimTool(): Identifier("DEFAULT_TOOL_INVALID"), Name(""), -Colour(PIXPACK(0xFFFFFF)), +Colour(0xFFFFFF_rgb), Description("NULL Tool, does NOTHING") { } diff --git a/src/simulation/SimTool.h b/src/simulation/SimTool.h index cd5fe700c..e69f3bd3f 100644 --- a/src/simulation/SimTool.h +++ b/src/simulation/SimTool.h @@ -1,6 +1,4 @@ -#ifndef SIMTOOL_H -#define SIMTOOL_H - +#pragma once #include "common/String.h" #include "graphics/Pixel.h" @@ -11,7 +9,7 @@ class SimTool public: ByteString Identifier; String Name; - pixel Colour; + RGB Colour; String Description; int (*Perform)(Simulation * sim, Particle * cpart, int x, int y, int brushX, int brushY, float strength); @@ -22,5 +20,3 @@ public: #include "ToolNumbers.h" #undef TOOL_NUMBERS_DECLARE }; - -#endif diff --git a/src/simulation/Simulation.cpp b/src/simulation/Simulation.cpp index 8afeed64c..3bd06def9 100644 --- a/src/simulation/Simulation.cpp +++ b/src/simulation/Simulation.cpp @@ -1,41 +1,17 @@ #include "Simulation.h" - -#include -#include -#include -#ifdef _MSC_VER -#include -#else -#include -#endif - #include "Air.h" -#include "Config.h" -#include "CoordStack.h" #include "ElementClasses.h" -#include "Gravity.h" -#include "Sample.h" -#include "Snapshot.h" - -#include "Misc.h" +#include "gravity/Gravity.h" #include "ToolClasses.h" -#include "Config.h" #include "SimulationData.h" #include "GOLString.h" - -#include "graphics/Renderer.h" - #include "client/GameSave.h" #include "common/tpt-compat.h" -#include "common/tpt-minmax.h" #include "common/tpt-rand.h" #include "common/tpt-thread-local.h" #include "gui/game/Brush.h" - -#ifdef LUACONSOLE -#include "lua/LuaScriptInterface.h" -#include "lua/LuaScriptHelper.h" -#endif +#include +#include extern int Element_PPIP_ppip_changed; extern int Element_LOLZ_RuleTable[9][9]; @@ -43,22 +19,9 @@ extern int Element_LOLZ_lolz[XRES/9][YRES/9]; extern int Element_LOVE_RuleTable[9][9]; extern int Element_LOVE_love[XRES/9][YRES/9]; -int Simulation::Load(const GameSave * save, bool includePressure) +void Simulation::Load(const GameSave *save, bool includePressure, Vec2 blockP) // block coordinates { - return Load(save, includePressure, 0, 0); -} - -int Simulation::Load(const GameSave * originalSave, bool includePressure, int fullX, int fullY) -{ - if (!originalSave) - return 1; - auto save = std::unique_ptr(new GameSave(*originalSave)); - - //Align to blockMap - int blockX = (fullX + CELL/2)/CELL; - int blockY = (fullY + CELL/2)/CELL; - fullX = blockX*CELL; - fullY = blockY*CELL; + auto partP = blockP * CELL; unsigned int pmapmask = (1<pmapbits)-1; int partMap[PT_NUM]; @@ -87,129 +50,82 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu } } - int r; - bool doFullScan = false; - for (int n = 0; n < NPART && n < save->particlesCount; n++) - { - Particle *tempPart = &save->particles[n]; - tempPart->x += (float)fullX; - tempPart->y += (float)fullY; - int x = int(tempPart->x + 0.5f); - int y = int(tempPart->y + 0.5f); + RecalcFreeParticles(false); - // Check various scenarios where we are unable to spawn the element, and set type to 0 to block spawning later - if (!InBounds(x, y)) - { - tempPart->type = 0; - continue; - } + auto &possiblyCarriesType = Particle::PossiblyCarriesType(); + auto &properties = Particle::GetProperties(); - int type = tempPart->type; - if (type < 0 || type >= PT_NUM) - { - tempPart->type = 0; - continue; - } - // Ensure we can spawn this element - if ((player.spwn == 1 && tempPart->type==PT_STKM) || (player2.spwn == 1 && tempPart->type==PT_STKM2)) - { - tempPart->type = 0; - continue; - } - if ((tempPart->type == PT_SPAWN || tempPart->type == PT_SPAWN2) && elementCount[type]) - { - tempPart->type = 0; - continue; - } - bool Element_FIGH_CanAlloc(Simulation *sim); - if (tempPart->type == PT_FIGH && !Element_FIGH_CanAlloc(this)) - { - tempPart->type = 0; - continue; - } - if (!elements[type].Enabled) - { - tempPart->type = 0; - continue; - } - - if ((r = pmap[y][x])) - { - // Particle already exists in this location. Set pmap to 0, then kill it and all stacked particles in the loop below - pmap[y][x] = 0; - doFullScan = true; - } - else if ((r = photons[y][x])) - { - // Particle already exists in this location. Set photons to 0, then kill it and all stacked particles in the loop below - photons[y][x] = 0; - doFullScan = true; - } - } - - if (doFullScan) - { - // Loop through particles to find particles in need of being killed - for (int i = 0; i <= parts_lastActiveIndex; i++) { - if (parts[i].type) - { - int x = int(parts[i].x + 0.5f); - int y = int(parts[i].y + 0.5f); - if (elements[parts[i].type].Properties & TYPE_ENERGY) - { - if (!photons[y][x]) - kill_part(i); - } - else - { - if (!pmap[y][x]) - kill_part(i); - } - } - } - } - - int i; - // Map of soap particles loaded into this save, old ID -> new ID std::map soapList; for (int n = 0; n < NPART && n < save->particlesCount; n++) { Particle tempPart = save->particles[n]; - if (tempPart.type > 0 && tempPart.type < PT_NUM) - tempPart.type = partMap[tempPart.type]; - else + if (tempPart.type <= 0 || tempPart.type >= PT_NUM) + { continue; + } - // These store type in ctype, but are special because they store extra information in the bits after type - if (tempPart.type == PT_CRAY || tempPart.type == PT_DRAY || tempPart.type == PT_CONV) + tempPart.x += (float)partP.X; + tempPart.y += (float)partP.Y; + int x = int(tempPart.x + 0.5f); + int y = int(tempPart.y + 0.5f); + + + // Check various scenarios where we are unable to spawn the element, and set type to 0 to block spawning later + if (!InBounds(x, y)) { - int ctype = tempPart.ctype & pmapmask; - int extra = tempPart.ctype >> save->pmapbits; - if (ctype >= 0 && ctype < PT_NUM) - ctype = partMap[ctype]; - tempPart.ctype = PMAP(extra, ctype); + continue; } - else if (GameSave::TypeInCtype(tempPart.type, tempPart.ctype)) + + tempPart.type = partMap[tempPart.type]; + for (auto index : possiblyCarriesType) { - tempPart.ctype = partMap[tempPart.ctype]; + if (elements[tempPart.type].CarriesTypeIn & (1U << index)) + { + auto *prop = reinterpret_cast(reinterpret_cast(&tempPart) + properties[index].Offset); + auto carriedType = *prop & int(pmapmask); + auto extra = *prop >> save->pmapbits; + if (carriedType >= 0 && carriedType < PT_NUM) + { + carriedType = partMap[carriedType]; + } + *prop = PMAP(extra, carriedType); + } } - // also stores extra bits past type (only STOR right now) - if (GameSave::TypeInTmp(tempPart.type)) + + // Ensure we can spawn this element + if ((player.spwn == 1 && tempPart.type==PT_STKM) || (player2.spwn == 1 && tempPart.type==PT_STKM2)) { - int tmp = tempPart.tmp & pmapmask; - int extra = tempPart.tmp >> save->pmapbits; - tmp = partMap[TYP(tmp)]; - tempPart.tmp = PMAP(extra, tmp); + continue; } - if (GameSave::TypeInTmp2(tempPart.type, tempPart.tmp2)) + if ((tempPart.type == PT_SPAWN || tempPart.type == PT_SPAWN2) && elementCount[tempPart.type]) { - tempPart.tmp2 = partMap[tempPart.tmp2]; + continue; + } + bool Element_FIGH_CanAlloc(Simulation *sim); + if (tempPart.type == PT_FIGH && !Element_FIGH_CanAlloc(this)) + { + continue; + } + if (!elements[tempPart.type].Enabled) + { + continue; + } + + // Mark location to be cleaned of existing particles. + pmap[y][x] = -1; + + if (elements[tempPart.type].CreateAllowed) + { + if (!(*(elements[tempPart.type].CreateAllowed))(this, -3, int(tempPart.x + 0.5f), int(tempPart.y + 0.5f), tempPart.type)) + { + continue; + } } // Allocate particle (this location is guaranteed to be empty due to "full scan" logic above) if (pfree == -1) break; - i = pfree; + auto i = pfree; pfree = parts[i].life; if (i > parts_lastActiveIndex) parts_lastActiveIndex = i; @@ -300,10 +216,35 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu { parts[i].tmp3 = 0; } + + if (!parts[i].type) + { + continue; + } + + // Mark to be preserved in the loop below. + parts[i].type |= 1 << PMAPBITS; } parts_lastActiveIndex = NPART-1; force_stacking_check = true; Element_PPIP_ppip_changed = 1; + + // Loop through particles to find particles in need of being killed + for (int i = 0; i <= parts_lastActiveIndex; i++) + { + if (parts[i].type) + { + int x = int(parts[i].x + 0.5f); + int y = int(parts[i].y + 0.5f); + bool preserve = parts[i].type & (1 << PMAPBITS); + parts[i].type &= ~(1 << PMAPBITS); + if (pmap[y][x] == -1 && !preserve) + { + kill_part(i); + } + } + } + RecalcFreeParticles(false); // fix SOAP links using soapList, a map of old particle ID -> new particle ID @@ -331,78 +272,64 @@ int Simulation::Load(const GameSave * originalSave, bool includePressure, int fu for (size_t i = 0; i < save->signs.size() && signs.size() < MAXSIGNS; i++) { - if (save->signs[i].text[0]) + if (save->signs[i].text.length()) { sign tempSign = save->signs[i]; - tempSign.x += fullX; - tempSign.y += fullY; + tempSign.x += partP.X; + tempSign.y += partP.Y; + if (!InBounds(tempSign.x, tempSign.y)) + { + continue; + } signs.push_back(tempSign); } } - for(int saveBlockX = 0; saveBlockX < save->blockWidth; saveBlockX++) + for (auto bpos : RectSized(blockP, save->blockSize) & CELLS.OriginRect()) { - for(int saveBlockY = 0; saveBlockY < save->blockHeight; saveBlockY++) + auto spos = bpos - blockP; + if (save->blockMap[spos]) { - if(save->blockMap[saveBlockY][saveBlockX]) + bmap[bpos.Y][bpos.X] = save->blockMap[spos]; + fvx[bpos.Y][bpos.X] = save->fanVelX[spos]; + fvy[bpos.Y][bpos.X] = save->fanVelY[spos]; + } + if (includePressure) + { + if (save->hasPressure) { - bmap[saveBlockY+blockY][saveBlockX+blockX] = save->blockMap[saveBlockY][saveBlockX]; - fvx[saveBlockY+blockY][saveBlockX+blockX] = save->fanVelX[saveBlockY][saveBlockX]; - fvy[saveBlockY+blockY][saveBlockX+blockX] = save->fanVelY[saveBlockY][saveBlockX]; + pv[bpos.Y][bpos.X] = save->pressure[spos]; + vx[bpos.Y][bpos.X] = save->velocityX[spos]; + vy[bpos.Y][bpos.X] = save->velocityY[spos]; } - if (includePressure) + if (save->hasAmbientHeat) { - if (save->hasPressure) - { - pv[saveBlockY+blockY][saveBlockX+blockX] = save->pressure[saveBlockY][saveBlockX]; - vx[saveBlockY+blockY][saveBlockX+blockX] = save->velocityX[saveBlockY][saveBlockX]; - vy[saveBlockY+blockY][saveBlockX+blockX] = save->velocityY[saveBlockY][saveBlockX]; - } - if (save->hasAmbientHeat) - hv[saveBlockY+blockY][saveBlockX+blockX] = save->ambientHeat[saveBlockY][saveBlockX]; + hv[bpos.Y][bpos.X] = save->ambientHeat[spos]; + } + if (save->hasBlockAirMaps) + { + air->bmap_blockair[bpos.Y][bpos.X] = save->blockAir[spos]; + air->bmap_blockairh[bpos.Y][bpos.X] = save->blockAirh[spos]; } } } gravWallChanged = true; - air->RecalculateBlockAirMaps(); - - return 0; + if (!save->hasBlockAirMaps) + { + air->ApproximateBlockAirMaps(); + } } -GameSave * Simulation::Save(bool includePressure) +std::unique_ptr Simulation::Save(bool includePressure, Rect partR) // particle coordinates { - return Save(includePressure, 0, 0, XRES-1, YRES-1); -} + auto blockR = RectBetween(partR.TopLeft / CELL, partR.BottomRight / CELL); + auto blockP = blockR.TopLeft; -GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int fullX2, int fullY2) -{ - int blockX, blockY, blockX2, blockY2, blockW, blockH; - //Normalise incoming coords - int swapTemp; - if(fullY>fullY2) - { - swapTemp = fullY; - fullY = fullY2; - fullY2 = swapTemp; - } - if(fullX>fullX2) - { - swapTemp = fullX; - fullX = fullX2; - fullX2 = swapTemp; - } - - //Align coords to blockMap - blockX = fullX/CELL; - blockY = fullY/CELL; - - blockX2 = (fullX2+CELL)/CELL; - blockY2 = (fullY2+CELL)/CELL; - - blockW = blockX2-blockX; - blockH = blockY2-blockY; - - GameSave * newSave = new GameSave(blockW, blockH); + auto newSave = std::make_unique(blockR.Size()); + auto &possiblyCarriesType = Particle::PossiblyCarriesType(); + auto &properties = Particle::GetProperties(); + newSave->frameCount = frameCount; + newSave->rngState = rng.state(); int storedParts = 0; int elementCount[PT_NUM]; @@ -416,11 +343,11 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full int x, y; x = int(parts[i].x + 0.5f); y = int(parts[i].y + 0.5f); - if (parts[i].type && x >= fullX && y >= fullY && x <= fullX2 && y <= fullY2) + if (parts[i].type && partR.Contains({ x, y })) { Particle tempPart = parts[i]; - tempPart.x -= blockX*CELL; - tempPart.y -= blockY*CELL; + tempPart.x -= blockP.X * CELL; + tempPart.y -= blockP.Y * CELL; if (elements[tempPart.type].Enabled) { particleMap.insert(std::pair(i, storedParts)); @@ -429,12 +356,14 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full elementCount[tempPart.type]++; paletteSet.insert(tempPart.type); - if (GameSave::TypeInCtype(tempPart.type, tempPart.ctype)) - paletteSet.insert(tempPart.ctype); - if (GameSave::TypeInTmp(tempPart.type)) - paletteSet.insert(TYP(tempPart.tmp)); - if (GameSave::TypeInTmp2(tempPart.type, tempPart.tmp2)) - paletteSet.insert(tempPart.tmp2); + for (auto index : possiblyCarriesType) + { + if (elements[tempPart.type].CarriesTypeIn & (1U << index)) + { + auto *prop = reinterpret_cast(reinterpret_cast(&tempPart) + properties[index].Offset); + paletteSet.insert(TYP(*prop)); + } + } } } } @@ -478,39 +407,39 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full for (size_t i = 0; i < MAXSIGNS && i < signs.size(); i++) { - if(signs[i].text.length() && signs[i].x >= fullX && signs[i].y >= fullY && signs[i].x <= fullX2 && signs[i].y <= fullY2) + if (signs[i].text.length() && partR.Contains({ signs[i].x, signs[i].y })) { sign tempSign = signs[i]; - tempSign.x -= blockX*CELL; - tempSign.y -= blockY*CELL; + tempSign.x -= blockP.X * CELL; + tempSign.y -= blockP.Y * CELL; *newSave << tempSign; } } - for(int saveBlockX = 0; saveBlockX < newSave->blockWidth; saveBlockX++) + for (auto bpos : newSave->blockSize.OriginRect()) { - for(int saveBlockY = 0; saveBlockY < newSave->blockHeight; saveBlockY++) + if(bmap[bpos.Y + blockP.Y][bpos.X + blockP.X]) { - if(bmap[saveBlockY+blockY][saveBlockX+blockX]) - { - newSave->blockMap[saveBlockY][saveBlockX] = bmap[saveBlockY+blockY][saveBlockX+blockX]; - newSave->fanVelX[saveBlockY][saveBlockX] = fvx[saveBlockY+blockY][saveBlockX+blockX]; - newSave->fanVelY[saveBlockY][saveBlockX] = fvy[saveBlockY+blockY][saveBlockX+blockX]; - } - if (includePressure) - { - newSave->pressure[saveBlockY][saveBlockX] = pv[saveBlockY+blockY][saveBlockX+blockX]; - newSave->velocityX[saveBlockY][saveBlockX] = vx[saveBlockY+blockY][saveBlockX+blockX]; - newSave->velocityY[saveBlockY][saveBlockX] = vy[saveBlockY+blockY][saveBlockX+blockX]; - newSave->ambientHeat[saveBlockY][saveBlockX] = hv[saveBlockY+blockY][saveBlockX+blockX]; - } + newSave->blockMap[bpos] = bmap[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->fanVelX[bpos] = fvx[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->fanVelY[bpos] = fvy[bpos.Y + blockP.Y][bpos.X + blockP.X]; + } + if (includePressure) + { + newSave->pressure[bpos] = pv[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->velocityX[bpos] = vx[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->velocityY[bpos] = vy[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->ambientHeat[bpos] = hv[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->blockAir[bpos] = air->bmap_blockair[bpos.Y + blockP.Y][bpos.X + blockP.X]; + newSave->blockAirh[bpos] = air->bmap_blockairh[bpos.Y + blockP.Y][bpos.X + blockP.X]; } } - if (includePressure) + if (includePressure || ensureDeterminism) { newSave->hasPressure = true; newSave->hasAmbientHeat = true; } + newSave->ensureDeterminism = ensureDeterminism; newSave->stkm.rocketBoots1 = player.rocketBoots; newSave->stkm.rocketBoots2 = player2.rocketBoots; @@ -524,117 +453,23 @@ GameSave * Simulation::Save(bool includePressure, int fullX, int fullY, int full newSave->stkm.fanFigh.push_back(i); } - SaveSimOptions(newSave); + SaveSimOptions(*newSave); newSave->pmapbits = PMAPBITS; return newSave; } -void Simulation::SaveSimOptions(GameSave * gameSave) +void Simulation::SaveSimOptions(GameSave &gameSave) { - if (!gameSave) - return; - gameSave->gravityMode = gravityMode; - gameSave->customGravityX = customGravityX; - gameSave->customGravityY = customGravityY; - gameSave->airMode = air->airMode; - gameSave->ambientAirTemp = air->ambientAirTemp; - gameSave->edgeMode = edgeMode; - gameSave->legacyEnable = legacy_enable; - gameSave->waterEEnabled = water_equal_test; - gameSave->gravityEnable = grav->IsEnabled(); - gameSave->aheatEnable = aheat_enable; -} - -std::unique_ptr Simulation::CreateSnapshot() -{ - auto snap = std::make_unique(); - snap->AirPressure .insert (snap->AirPressure .begin(), &pv [0][0] , &pv [0][0] + ((XRES / CELL) * (YRES / CELL))); - snap->AirVelocityX .insert (snap->AirVelocityX .begin(), &vx [0][0] , &vx [0][0] + ((XRES / CELL) * (YRES / CELL))); - snap->AirVelocityY .insert (snap->AirVelocityY .begin(), &vy [0][0] , &vy [0][0] + ((XRES / CELL) * (YRES / CELL))); - snap->AmbientHeat .insert (snap->AmbientHeat .begin(), &hv [0][0] , &hv [0][0] + ((XRES / CELL) * (YRES / CELL))); - snap->BlockMap .insert (snap->BlockMap .begin(), &bmap[0][0] , &bmap[0][0] + ((XRES / CELL) * (YRES / CELL))); - snap->ElecMap .insert (snap->ElecMap .begin(), &emap[0][0] , &emap[0][0] + ((XRES / CELL) * (YRES / CELL))); - snap->FanVelocityX .insert (snap->FanVelocityX .begin(), &fvx [0][0] , &fvx [0][0] + ((XRES / CELL) * (YRES / CELL))); - snap->FanVelocityY .insert (snap->FanVelocityY .begin(), &fvy [0][0] , &fvy [0][0] + ((XRES / CELL) * (YRES / CELL))); - snap->GravVelocityX .insert (snap->GravVelocityX .begin(), &gravx [0] , &gravx [0] + ((XRES / CELL) * (YRES / CELL))); - snap->GravVelocityY .insert (snap->GravVelocityY .begin(), &gravy [0] , &gravy [0] + ((XRES / CELL) * (YRES / CELL))); - snap->GravValue .insert (snap->GravValue .begin(), &gravp [0] , &gravp [0] + ((XRES / CELL) * (YRES / CELL))); - snap->GravMap .insert (snap->GravMap .begin(), &gravmap[0] , &gravmap[0] + ((XRES / CELL) * (YRES / CELL))); - snap->Particles .insert (snap->Particles .begin(), &parts [0] , &parts[parts_lastActiveIndex + 1] ); - snap->PortalParticles.insert (snap->PortalParticles.begin(), &portalp[0][0][0], &portalp [CHANNELS - 1][8 - 1][80 - 1] ); - snap->WirelessData .insert (snap->WirelessData .begin(), &wireless[0][0] , &wireless[CHANNELS - 1][2 - 1] ); - snap->stickmen .insert (snap->stickmen .begin(), &fighters[0] , &fighters[MAX_FIGHTERS] ); - snap->stickmen .push_back(player2); - snap->stickmen .push_back(player); - snap->signs = signs; - return snap; -} - -void Simulation::Restore(const Snapshot &snap) -{ - std::fill(elementCount, elementCount + PT_NUM, 0); - elementRecount = true; - force_stacking_check = true; - for (auto &part : parts) - { - part.type = 0; - } - std::copy(snap.AirPressure .begin(), snap.AirPressure .end(), &pv[0][0] ); - std::copy(snap.AirVelocityX .begin(), snap.AirVelocityX .end(), &vx[0][0] ); - std::copy(snap.AirVelocityY .begin(), snap.AirVelocityY .end(), &vy[0][0] ); - std::copy(snap.AmbientHeat .begin(), snap.AmbientHeat .end(), &hv[0][0] ); - std::copy(snap.BlockMap .begin(), snap.BlockMap .end(), &bmap[0][0] ); - std::copy(snap.ElecMap .begin(), snap.ElecMap .end(), &emap[0][0] ); - std::copy(snap.FanVelocityX .begin(), snap.FanVelocityX .end(), &fvx[0][0] ); - std::copy(snap.FanVelocityY .begin(), snap.FanVelocityY .end(), &fvy[0][0] ); - if (grav->IsEnabled()) - { - grav->Clear(); - std::copy(snap.GravVelocityX.begin(), snap.GravVelocityX.end(), &gravx [0] ); - std::copy(snap.GravVelocityY.begin(), snap.GravVelocityY.end(), &gravy [0] ); - std::copy(snap.GravValue .begin(), snap.GravValue .end(), &gravp [0] ); - std::copy(snap.GravMap .begin(), snap.GravMap .end(), &gravmap[0] ); - } - std::copy(snap.Particles .begin(), snap.Particles .end(), &parts[0] ); - std::copy(snap.PortalParticles.begin(), snap.PortalParticles.end(), &portalp[0][0][0]); - std::copy(snap.WirelessData .begin(), snap.WirelessData .end(), &wireless[0][0] ); - std::copy(snap.stickmen .begin(), snap.stickmen.end() - 2 , &fighters[0] ); - player = snap.stickmen[snap.stickmen.size() - 1]; - player2 = snap.stickmen[snap.stickmen.size() - 2]; - signs = snap.signs; - parts_lastActiveIndex = NPART - 1; - air->RecalculateBlockAirMaps(); - RecalcFreeParticles(false); - gravWallChanged = true; -} - -void Simulation::clear_area(int area_x, int area_y, int area_w, int area_h) -{ - float fx = area_x-.5f, fy = area_y-.5f; - for (int i = 0; i <= parts_lastActiveIndex; i++) - { - if (parts[i].type) - if (parts[i].x >= fx && parts[i].x <= fx+area_w+1 && parts[i].y >= fy && parts[i].y <= fy+area_h+1) - kill_part(i); - } - int cx1 = area_x/CELL, cy1 = area_y/CELL, cx2 = (area_x+area_w)/CELL, cy2 = (area_y+area_h)/CELL; - for (int y = cy1; y <= cy2; y++) - { - for (int x = cx1; x <= cx2; x++) - { - if (bmap[y][x] == WL_GRAV) - gravWallChanged = true; - bmap[y][x] = 0; - emap[y][x] = 0; - } - } - for( int i = signs.size()-1; i >= 0; i--) - { - if (signs[i].text.length() && signs[i].x >= area_x && signs[i].y >= area_y && signs[i].x <= area_x+area_w && signs[i].y <= area_y+area_h) - { - signs.erase(signs.begin()+i); - } - } + gameSave.gravityMode = gravityMode; + gameSave.customGravityX = customGravityX; + gameSave.customGravityY = customGravityY; + gameSave.airMode = air->airMode; + gameSave.ambientAirTemp = air->ambientAirTemp; + gameSave.edgeMode = edgeMode; + gameSave.legacyEnable = legacy_enable; + gameSave.waterEEnabled = water_equal_test; + gameSave.gravityEnable = grav->IsEnabled(); + gameSave.aheatEnable = aheat_enable; } bool Simulation::FloodFillPmapCheck(int x, int y, int type) @@ -736,46 +571,6 @@ int Simulation::flood_prop(int x, int y, size_t propoffset, PropertyValue propva return did_something; } -SimulationSample Simulation::GetSample(int x, int y) -{ - SimulationSample sample; - sample.PositionX = x; - sample.PositionY = y; - if (x >= 0 && x < XRES && y >= 0 && y < YRES) - { - if (photons[y][x]) - { - sample.particle = parts[ID(photons[y][x])]; - sample.ParticleID = ID(photons[y][x]); - } - else if (pmap[y][x]) - { - sample.particle = parts[ID(pmap[y][x])]; - sample.ParticleID = ID(pmap[y][x]); - } - if (bmap[y/CELL][x/CELL]) - { - sample.WallType = bmap[y/CELL][x/CELL]; - } - sample.AirPressure = pv[y/CELL][x/CELL]; - sample.AirTemperature = hv[y/CELL][x/CELL]; - sample.AirVelocityX = vx[y/CELL][x/CELL]; - sample.AirVelocityY = vy[y/CELL][x/CELL]; - - if(grav->IsEnabled()) - { - sample.Gravity = gravp[(y/CELL)*(XRES/CELL)+(x/CELL)]; - sample.GravityVelocityX = gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]; - sample.GravityVelocityY = gravy[(y/CELL)*(XRES/CELL)+(x/CELL)]; - } - } - else - sample.isMouseInSim = false; - - sample.NumParts = NUM_PARTS; - return sample; -} - int Simulation::FloodINST(int x, int y) { int x1, x2; @@ -885,7 +680,7 @@ int Simulation::FloodINST(int x, int y) bool Simulation::flood_water(int x, int y, int i) { - int x1, x2, originalY = y; + int x1, x2, originalX = x, originalY = y; int r = pmap[y][x]; if (!r) return false; @@ -893,7 +688,7 @@ bool Simulation::flood_water(int x, int y, int i) // Bitmap for checking where we've already looked auto bitmapPtr = std::unique_ptr(new char[XRES * YRES]); char *bitmap = bitmapPtr.get(); - std::fill(&bitmap[0], &bitmap[XRES * YRES], 0); + std::fill(&bitmap[0], &bitmap[0] + XRES * YRES, 0); try { @@ -922,19 +717,14 @@ bool Simulation::flood_water(int x, int y, int i) if ((y - 1) > originalY && !pmap[y - 1][x]) { // Try to move the water to a random position on this line, because there's probably a free location somewhere - int randPos = RNG::Ref().between(x, x2); + int randPos = rng.between(x, x2); if (!pmap[y - 1][randPos] && eval_move(parts[i].type, randPos, y - 1, nullptr)) x = randPos; // Couldn't move to random position, so try the original position on the left else if (!eval_move(parts[i].type, x, y - 1, nullptr)) continue; - int oldx = (int)(parts[i].x + 0.5f); - int oldy = (int)(parts[i].y + 0.5f); - pmap[y - 1][x] = pmap[oldy][oldx]; - pmap[oldy][oldx] = 0; - parts[i].x = float(x); - parts[i].y = float(y - 1); + move(i, originalX, originalY, float(x), float(y - 1)); return true; } @@ -958,29 +748,6 @@ bool Simulation::flood_water(int x, int y, int i) return false; } -void Simulation::SetDecoSpace(int newDecoSpace) -{ - switch (newDecoSpace) - { - case 0: // sRGB - default: // anything stupid - deco_space = 0; - break; - - case 1: // linear - deco_space = 1; - break; - - case 2: // Gamma = 2.2 - deco_space = 2; - break; - - case 3: // Gamma = 1.8 - deco_space = 3; - break; - } -} - void Simulation::SetEdgeMode(int newEdgeMode) { edgeMode = newEdgeMode; @@ -988,28 +755,28 @@ void Simulation::SetEdgeMode(int newEdgeMode) { case 0: case 2: - for(int i = 0; i<(XRES/CELL); i++) + for(int i = 0; i> (x+18)) & 1; - tg += (parts[ID(rp)].ctype >> (x+9)) & 1; - tb += (parts[ID(rp)].ctype >> x) & 1; - } - - bool tozero = false; - if (parts[ID(rp)].life <= 0) - { - tozero = true; - parts[ID(rp)].life = 680; - } - - double xl = 255.0 / std::max(std::max(tr,tg),tb) * std::min(parts[ID(rp)].life, 680) / 680.0; - tr = round(tr * xl); - tg = round(tg * xl); - tb = round(tb * xl); - - if (tozero) - parts[ID(rp)].life = 0; - } - else - { - ta = float((parts[ID(rp)].dcolour>>24)&0xFF); - tr = float((parts[ID(rp)].dcolour>>16)&0xFF); - tg = float((parts[ID(rp)].dcolour>>8)&0xFF); - tb = float((parts[ID(rp)].dcolour)&0xFF); - } - - ta /= 255.0f; tr /= 255.0f; tg /= 255.0f; tb /= 255.0f; - colR /= 255.0f; colG /= 255.0f; colB /= 255.0f; colA /= 255.0f; - - if (mode == DECO_DRAW) - { - ta = colA; - tr = colR; - tg = colG; - tb = colB; - } - else if (mode == DECO_CLEAR) - { - ta = tr = tg = tb = 0.0f; - } - else if (mode == DECO_ADD) - { - //ta += (colA*strength)*colA; - tr += (colR*strength)*colA; - tg += (colG*strength)*colA; - tb += (colB*strength)*colA; - } - else if (mode == DECO_SUBTRACT) - { - //ta -= (colA*strength)*colA; - tr -= (colR*strength)*colA; - tg -= (colG*strength)*colA; - tb -= (colB*strength)*colA; - } - else if (mode == DECO_MULTIPLY) - { - tr *= 1.0f+(colR*strength)*colA; - tg *= 1.0f+(colG*strength)*colA; - tb *= 1.0f+(colB*strength)*colA; - } - else if (mode == DECO_DIVIDE) - { - tr /= 1.0f+(colR*strength)*colA; - tg /= 1.0f+(colG*strength)*colA; - tb /= 1.0f+(colB*strength)*colA; - } - else if (mode == DECO_SMUDGE) - { - if (x >= CELL && x < XRES-CELL && y >= CELL && y < YRES-CELL) - { - float tas = 0.0f, trs = 0.0f, tgs = 0.0f, tbs = 0.0f; - - int rx, ry; - float num = 0; - for (rx=-2; rx<3; rx++) - for (ry=-2; ry<3; ry++) - { - if (abs(rx)+abs(ry) > 2 && TYP(pmap[y+ry][x+rx]) && parts[ID(pmap[y+ry][x+rx])].dcolour) - { - Particle part = parts[ID(pmap[y+ry][x+rx])]; - num += 1.0f; - float pa = ((float)((part.dcolour>>24)&0xFF)) / 255.f; - float pr = ((float)((part.dcolour>>16)&0xFF)) / 255.f; - float pg = ((float)((part.dcolour>> 8)&0xFF)) / 255.f; - float pb = ((float)((part.dcolour )&0xFF)) / 255.f; - switch (deco_space) - { - case 0: // sRGB - pa = (pa <= 0.04045f) ? (pa / 12.92f) : pow((pa + 0.055f) / 1.055f, 2.4f); - pr = (pr <= 0.04045f) ? (pr / 12.92f) : pow((pr + 0.055f) / 1.055f, 2.4f); - pg = (pg <= 0.04045f) ? (pg / 12.92f) : pow((pg + 0.055f) / 1.055f, 2.4f); - pb = (pb <= 0.04045f) ? (pb / 12.92f) : pow((pb + 0.055f) / 1.055f, 2.4f); - break; - - case 1: // linear - break; - - case 2: // Gamma = 2.2 - pa = pow(pa, 2.2f); - pr = pow(pr, 2.2f); - pg = pow(pg, 2.2f); - pb = pow(pb, 2.2f); - break; - - case 3: // Gamma = 1.8 - pa = pow(pa, 1.8f); - pr = pow(pr, 1.8f); - pg = pow(pg, 1.8f); - pb = pow(pb, 1.8f); - break; - } - tas += pa; - trs += pr; - tgs += pg; - tbs += pb; - } - } - if (num == 0) - return; - ta = tas / num; - tr = trs / num; - tg = tgs / num; - tb = tbs / num; - switch (deco_space) - { - case 0: // sRGB - ta = (ta <= 0.0031308f) ? (ta * 12.92f) : (1.055f * pow(ta, 1.f / 2.4f) - 0.055f); - tr = (tr <= 0.0031308f) ? (tr * 12.92f) : (1.055f * pow(tr, 1.f / 2.4f) - 0.055f); - tg = (tg <= 0.0031308f) ? (tg * 12.92f) : (1.055f * pow(tg, 1.f / 2.4f) - 0.055f); - tb = (tb <= 0.0031308f) ? (tb * 12.92f) : (1.055f * pow(tb, 1.f / 2.4f) - 0.055f); - break; - - case 1: // linear - break; - - case 2: // Gamma = 2.2 - ta = pow(ta, 1.f / 2.2f); - tr = pow(tr, 1.f / 2.2f); - tg = pow(tg, 1.f / 2.2f); - tb = pow(tb, 1.f / 2.2f); - break; - - case 3: // Gamma = 1.8 - ta = pow(ta, 1.f / 1.8f); - tr = pow(tr, 1.f / 1.8f); - tg = pow(tg, 1.f / 1.8f); - tb = pow(tb, 1.f / 1.8f); - break; - } - if (!parts[ID(rp)].dcolour) - ta -= 3/255.0f; - } - } - - ta *= 255.0f; tr *= 255.0f; tg *= 255.0f; tb *= 255.0f; - ta += .5f; tr += .5f; tg += .5f; tb += .5f; - - colA_ = int(ta); - colR_ = int(tr); - colG_ = int(tg); - colB_ = int(tb); - - if(colA_ > 255) - colA_ = 255; - else if(colA_ < 0) - colA_ = 0; - if(colR_ > 255) - colR_ = 255; - else if(colR_ < 0) - colR_ = 0; - if(colG_ > 255) - colG_ = 255; - else if(colG_ < 0) - colG_ = 0; - if(colB_ > 255) - colB_ = 255; - else if(colB_ < 0) - colB_ = 0; - if (TYP(rp) != PT_PHOT) - parts[ID(rp)].dcolour = ((colA_<<24)|(colR_<<16)|(colG_<<8)|colB_); - else - { - int cr = colR_, cg = colG_, cb = colB_; - float vl = std::max(std::max(cr, cg), cb); - if (vl == 0.0f) - vl = 1.0f; - int mt = 5; - int best = 1000; - int bestmt = mt; - int vr, vg, vb; - for (; mt < 13; mt++) - { - vr = (int)(cr / vl * mt + 0.5f); - vg = (int)(cg / vl * mt + 0.5f); - vb = (int)(cb / vl * mt + 0.5f); - if ((mt < 7 || vr + vb >= mt - 6) && (mt < 10 || vg >= std::max(vr - 9, 0) + std::max(vb - 9, 0))) - { - int diff = std::abs(cr - vr * vl / mt) + std::abs(cg - vg * vl / mt) + std::abs(cb - vb * vl / mt); - if (diff <= best) - { - best = diff; - bestmt = mt; - } - } - } - mt = bestmt; - vr = (int)(cr / vl * mt + 0.5f); - vg = (int)(cg / vl * mt + 0.5f); - vb = (int)(cb / vl * mt + 0.5f); - int shg = 0; - if (vg > 6) - { - shg = std::min(std::max(std::max(std::min(vr - vb, vg - 6), 6 - vg), -3), 3); - vr -= std::max(shg, 0); - vb += std::min(shg, 0); - } - else - { - if (vb > 9) - vg -= vb - 9; - if (vr > 9) - vg -= vr - 9; - } - unsigned int mask = ((1 << vr) - 1) << (30 - vr); - mask |= ((1 << vg) - 1) << (12 + shg); - mask |= ((1 << vb) - 1); - mask &= 0x3FFFFFFF; - parts[ID(rp)].ctype = mask; - } -} - -void Simulation::ApplyDecorationPoint(int positionX, int positionY, int colR, int colG, int colB, int colA, int mode, Brush * cBrush) -{ - if(cBrush) - { - int radiusX = cBrush->GetRadius().X, radiusY = cBrush->GetRadius().Y, sizeX = cBrush->GetSize().X, sizeY = cBrush->GetSize().Y; - unsigned char *bitmap = cBrush->GetBitmap(); - - for(int y = 0; y < sizeY; y++) - { - for(int x = 0; x < sizeX; x++) - { - if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES)) - { - ApplyDecoration(positionX+(x-radiusX), positionY+(y-radiusY), colR, colG, colB, colA, mode); - } - } - } - } -} - -void Simulation::ApplyDecorationLine(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode, Brush * cBrush) -{ - bool reverseXY = abs(y2-y1) > abs(x2-x1); - int x, y, dx, dy, sy, rx = 0, ry = 0; - float e = 0.0f, de; - - if(cBrush) - { - rx = cBrush->GetRadius().X; - ry = cBrush->GetRadius().Y; - } - - if (reverseXY) - { - y = x1; - x1 = y1; - y1 = y; - y = x2; - x2 = y2; - y2 = y; - } - if (x1 > x2) - { - y = x1; - x1 = x2; - x2 = y; - y = y1; - y1 = y2; - y2 = y; - } - dx = x2 - x1; - dy = abs(y2 - y1); - de = dx ? dy/(float)dx : 0.0f; - y = y1; - sy = (y1= 0.5f) - { - y += sy; - if (!(rx+ry)) - { - if (reverseXY) - ApplyDecorationPoint(y, x, colR, colG, colB, colA, mode, cBrush); - else - ApplyDecorationPoint(x, y, colR, colG, colB, colA, mode, cBrush); - } - e -= 1.0f; - } - } -} - -void Simulation::ApplyDecorationBox(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode) -{ - int i, j; - - if (x1>x2) - { - i = x2; - x2 = x1; - x1 = i; - } - if (y1>y2) - { - j = y2; - y2 = y1; - y1 = j; - } - for (j=y1; j<=y2; j++) - for (i=x1; i<=x2; i++) - ApplyDecoration(i, j, colR, colG, colB, colA, mode); -} - -bool Simulation::ColorCompare(Renderer *ren, int x, int y, int replaceR, int replaceG, int replaceB) -{ - pixel pix = ren->vid[x+y*WINDOWW]; - int r = PIXR(pix); - int g = PIXG(pix); - int b = PIXB(pix); - int diff = std::abs(replaceR-r) + std::abs(replaceG-g) + std::abs(replaceB-b); - return diff < 15; -} - -void Simulation::ApplyDecorationFill(Renderer *ren, int x, int y, int colR, int colG, int colB, int colA, int replaceR, int replaceG, int replaceB) -{ - int x1, x2; - char *bitmap = (char*)malloc(XRES*YRES); //Bitmap for checking - if (!bitmap) - return; - memset(bitmap, 0, XRES*YRES); - - if (!ColorCompare(ren, x, y, replaceR, replaceG, replaceB)) { - free(bitmap); - return; - } - - try - { - CoordStack& cs = getCoordStackSingleton(); - cs.clear(); - - cs.push(x, y); - do - { - cs.pop(x, y); - x1 = x2 = x; - // go left as far as possible - while (x1>0) - { - if (bitmap[(x1-1)+y*XRES] || !ColorCompare(ren, x1-1, y, replaceR, replaceG, replaceB)) - { - break; - } - x1--; - } - // go right as far as possible - while (x2= 1) - for (x=x1; x<=x2; x++) - if (!bitmap[x+(y-1)*XRES] && ColorCompare(ren, x, y-1, replaceR, replaceG, replaceB)) - cs.push(x, y-1); - - if (y < YRES-1) - for (x=x1; x<=x2; x++) - if (!bitmap[x+(y+1)*XRES] && ColorCompare(ren, x, y+1, replaceR, replaceG, replaceB)) - cs.push(x, y+1); - } while (cs.getSize() > 0); - } - catch (std::exception& e) - { - std::cerr << e.what() << std::endl; - free(bitmap); - return; - } - free(bitmap); -} -#endif - -int Simulation::Tool(int x, int y, int tool, int brushX, int brushY, float strength) -{ - Particle * cpart = NULL; - int r; - if ((r = pmap[y][x])) - cpart = &(parts[ID(r)]); - else if ((r = photons[y][x])) - cpart = &(parts[ID(r)]); - return tools[tool].Perform(this, cpart, x, y, brushX, brushY, strength); -} - -#ifndef RENDERER -int Simulation::ToolBrush(int positionX, int positionY, int tool, Brush * cBrush, float strength) -{ - if(cBrush) - { - int radiusX = cBrush->GetRadius().X, radiusY = cBrush->GetRadius().Y, sizeX = cBrush->GetSize().X, sizeY = cBrush->GetSize().Y; - unsigned char *bitmap = cBrush->GetBitmap(); - for(int y = 0; y < sizeY; y++) - for(int x = 0; x < sizeX; x++) - if(bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES)) - Tool(positionX + (x - radiusX), positionY + (y - radiusY), tool, positionX, positionY, strength); - } - return 0; -} - -void Simulation::ToolLine(int x1, int y1, int x2, int y2, int tool, Brush * cBrush, float strength) -{ - bool reverseXY = abs(y2-y1) > abs(x2-x1); - int x, y, dx, dy, sy, rx = cBrush->GetRadius().X, ry = cBrush->GetRadius().Y; - float e = 0.0f, de; - if (reverseXY) - { - y = x1; - x1 = y1; - y1 = y; - y = x2; - x2 = y2; - y2 = y; - } - if (x1 > x2) - { - y = x1; - x1 = x2; - x2 = y; - y = y1; - y1 = y2; - y2 = y; - } - dx = x2 - x1; - dy = abs(y2 - y1); - de = dx ? dy/(float)dx : 0.0f; - y = y1; - sy = (y1= 0.5f) - { - y += sy; - if (!(rx+ry) && ((y1=y2))) - { - if (reverseXY) - ToolBrush(y, x, tool, cBrush, strength); - else - ToolBrush(x, y, tool, cBrush, strength); - } - e -= 1.0f; - } - } -} -void Simulation::ToolBox(int x1, int y1, int x2, int y2, int tool, float strength) -{ - int brushX, brushY; - brushX = ((x1 + x2) / 2); - brushY = ((y1 + y2) / 2); - int i, j; - if (x1>x2) - { - i = x2; - x2 = x1; - x1 = i; - } - if (y1>y2) - { - j = y2; - y2 = y1; - y1 = j; - } - for (j=y1; j<=y2; j++) - for (i=x1; i<=x2; i++) - Tool(i, j, tool, brushX, brushY, strength); -} -#endif - -int Simulation::CreateWalls(int x, int y, int rx, int ry, int wall, Brush * cBrush) -{ - if(cBrush) - { - rx = cBrush->GetRadius().X; - ry = cBrush->GetRadius().Y; - } - - ry = ry/CELL; - rx = rx/CELL; - x = x/CELL; - y = y/CELL; - x -= rx; - y -= ry; - for (int wallX = x; wallX <= x+rx+rx; wallX++) - { - for (int wallY = y; wallY <= y+ry+ry; wallY++) - { - if (wallX >= 0 && wallX < XRES/CELL && wallY >= 0 && wallY < YRES/CELL) - { - if (wall == WL_FAN) - { - fvx[wallY][wallX] = 0.0f; - fvy[wallY][wallX] = 0.0f; - } - else if (wall == WL_STREAM) - { - wallX = x + rx; - wallY = y + ry; - //streamlines can't be drawn next to each other - for (int tempY = wallY-1; tempY < wallY+2; tempY++) - for (int tempX = wallX-1; tempX < wallX+2; tempX++) - { - if (tempX >= 0 && tempX < XRES/CELL && tempY >= 0 && tempY < YRES/CELL && bmap[tempY][tempX] == WL_STREAM) - return 1; - } - } - if (wall == WL_GRAV || bmap[wallY][wallX] == WL_GRAV) - gravWallChanged = true; - - if (wall == WL_ERASEALL) - { - for (int i = 0; i < CELL; i++) - for (int j = 0; j < CELL; j++) - { - delete_part(wallX*CELL+i, wallY*CELL+j); - } - for (int i = signs.size()-1; i >= 0; i--) - if (signs[i].x >= wallX*CELL && signs[i].y >= wallY*CELL && signs[i].x <= (wallX+1)*CELL && signs[i].y <= (wallY+1)*CELL) - signs.erase(signs.begin()+i); - bmap[wallY][wallX] = 0; - } - else - bmap[wallY][wallX] = wall; - } - } - } - return 1; -} - -void Simulation::CreateWallLine(int x1, int y1, int x2, int y2, int rx, int ry, int wall, Brush * cBrush) -{ - int x, y, dx, dy, sy; - bool reverseXY = abs(y2-y1) > abs(x2-x1); - float e = 0.0f, de; - if (reverseXY) - { - y = x1; - x1 = y1; - y1 = y; - y = x2; - x2 = y2; - y2 = y; - } - if (x1 > x2) - { - y = x1; - x1 = x2; - x2 = y; - y = y1; - y1 = y2; - y2 = y; - } - dx = x2 - x1; - dy = abs(y2 - y1); - de = dx ? dy/(float)dx : 0.0f; - y = y1; - sy = (y1= 0.5f) - { - y += sy; - if ((y1=y2)) - { - if (reverseXY) - CreateWalls(y, x, rx, ry, wall, cBrush); - else - CreateWalls(x, y, rx, ry, wall, cBrush); - } - e -= 1.0f; - } - } -} - -void Simulation::CreateWallBox(int x1, int y1, int x2, int y2, int wall) -{ - int i, j; - if (x1>x2) - { - i = x2; - x2 = x1; - x1 = i; - } - if (y1>y2) - { - j = y2; - y2 = y1; - y1 = j; - } - for (j=y1; j<=y2; j++) - for (i=x1; i<=x2; i++) - CreateWalls(i, j, 0, 0, wall, NULL); -} - -int Simulation::FloodWalls(int x, int y, int wall, int bm) -{ - int x1, x2, dy = CELL; - if (bm==-1) - { - if (wall==WL_ERASE || wall==WL_ERASEALL) - { - bm = bmap[y/CELL][x/CELL]; - if (!bm) - return 0; - } - else - bm = 0; - } - - if (bmap[y/CELL][x/CELL]!=bm) - return 1; - - // go left as far as possible - x1 = x2 = x; - while (x1>=CELL) - { - if (bmap[y/CELL][(x1-1)/CELL]!=bm) - { - break; - } - x1--; - } - while (x2=CELL) - for (x=x1; x<=x2; x++) - if (bmap[(y-dy)/CELL][x/CELL]==bm) - if (!FloodWalls(x, y-dy, wall, bm)) - return 0; - if (yGetRadius().X, radiusY = cBrush->GetRadius().Y, sizeX = cBrush->GetSize().X, sizeY = cBrush->GetSize().Y; - unsigned char *bitmap = cBrush->GetBitmap(); - - // special case for LIGH - if (c == PT_LIGH) - { - if (currentTick < lightningRecreate) - return 1; - int newlife = radiusX + radiusY; - if (newlife > 55) - newlife = 55; - c = PMAP(newlife, c); - lightningRecreate = currentTick + std::max(newlife / 4, 1); - return CreatePartFlags(positionX, positionY, c, flags); - } - else if (c == PT_TESC) - { - int newtmp = (radiusX*4+radiusY*4+7); - if (newtmp > 300) - newtmp = 300; - c = PMAP(newtmp, c); - } - - for (int y = sizeY-1; y >=0; y--) - { - for (int x = 0; x < sizeX; x++) - { - if (bitmap[(y*sizeX)+x] && (positionX+(x-radiusX) >= 0 && positionY+(y-radiusY) >= 0 && positionX+(x-radiusX) < XRES && positionY+(y-radiusY) < YRES)) - { - CreatePartFlags(positionX+(x-radiusX), positionY+(y-radiusY), c, flags); - } - } - } - } - return 0; -} - -int Simulation::CreateParts(int x, int y, int rx, int ry, int c, int flags) -{ - bool created = false; - - if (flags == -1) - flags = replaceModeFlags; - - // special case for LIGH - if (c == PT_LIGH) - { - if (currentTick < lightningRecreate) - return 1; - int newlife = rx + ry; - if (newlife > 55) - newlife = 55; - c = PMAP(newlife, c); - lightningRecreate = currentTick + std::max(newlife / 4, 1); - rx = ry = 0; - } - else if (c == PT_TESC) - { - int newtmp = (rx*4+ry*4+7); - if (newtmp > 300) - newtmp = 300; - c = PMAP(newtmp, c); - } - - for (int j = -ry; j <= ry; j++) - for (int i = -rx; i <= rx; i++) - if (CreatePartFlags(x+i, y+j, c, flags)) - created = true; - return !created; -} - -void Simulation::CreateLine(int x1, int y1, int x2, int y2, int c, Brush * cBrush, int flags) -{ - int x, y, dx, dy, sy, rx = cBrush->GetRadius().X, ry = cBrush->GetRadius().Y; - bool reverseXY = abs(y2-y1) > abs(x2-x1); - float e = 0.0f, de; - if (reverseXY) - { - y = x1; - x1 = y1; - y1 = y; - y = x2; - x2 = y2; - y2 = y; - } - if (x1 > x2) - { - y = x1; - x1 = x2; - x2 = y; - y = y1; - y1 = y2; - y2 = y; - } - dx = x2 - x1; - dy = abs(y2 - y1); - de = dx ? dy/(float)dx : 0.0f; - y = y1; - sy = (y1= 0.5f) - { - y += sy; - if (!(rx+ry) && ((y1=y2))) - { - if (reverseXY) - CreateParts(y, x, c, cBrush, flags); - else - CreateParts(x, y, c, cBrush, flags); - } - e -= 1.0f; - } - } -} -#endif - -int Simulation::CreatePartFlags(int x, int y, int c, int flags) -{ - if (x < 0 || y < 0 || x >= XRES || y >= YRES) - { - return 0; - } - - if (flags & REPLACE_MODE) - { - // if replace whatever and there's something to replace - // or replace X and there's a non-energy particle on top with type X - // or replace X and there's an energy particle on top with type X - if ((!replaceModeSelected && (photons[y][x] || pmap[y][x])) || - (!photons[y][x] && pmap[y][x] && TYP(pmap[y][x]) == replaceModeSelected) || - (photons[y][x] && TYP(photons[y][x]) == replaceModeSelected)) - { - if (c) - create_part(photons[y][x] ? ID(photons[y][x]) : ID(pmap[y][x]), x, y, TYP(c), ID(c)); - else - delete_part(x, y); - } - return 0; - } - else if (!c) - { - delete_part(x, y); - return 0; - } - else if (flags & SPECIFIC_DELETE) - { - // if delete whatever and there's something to delete - // or delete X and there's a non-energy particle on top with type X - // or delete X and there's an energy particle on top with type X - if ((!replaceModeSelected && (photons[y][x] || pmap[y][x])) || - (!photons[y][x] && pmap[y][x] && TYP(pmap[y][x]) == replaceModeSelected) || - (photons[y][x] && TYP(photons[y][x]) == replaceModeSelected)) - { - delete_part(x, y); - } - return 0; - } - else - { - return (create_part(-2, x, y, TYP(c), ID(c)) == -1); - } - - // I'm sure at least one compiler exists that would complain if this wasn't here - return 0; -} - -//Now simply creates a 0 pixel radius line without all the complicated flags / other checks +// Now simply creates a 0 pixel radius line without all the complicated flags / other checks +// Would make sense to move to Editing.cpp but SPRK needs it. void Simulation::CreateLine(int x1, int y1, int x2, int y2, int c) { bool reverseXY = abs(y2-y1) > abs(x2-x1); @@ -1963,160 +839,6 @@ void Simulation::CreateLine(int x1, int y1, int x2, int y2, int c) } } -#ifndef RENDERER -void Simulation::CreateBox(int x1, int y1, int x2, int y2, int c, int flags) -{ - int i, j; - if (x1>x2) - { - i = x2; - x2 = x1; - x1 = i; - } - if (y1>y2) - { - j = y2; - y2 = y1; - y1 = j; - } - for (j=y2; j>=y1; j--) - for (i=x1; i<=x2; i++) - CreateParts(i, j, 0, 0, c, flags); -} - -int Simulation::FloodParts(int x, int y, int fullc, int cm, int flags) -{ - int c = TYP(fullc); - int x1, x2, dy = (c(new char[XRES * YRES]); - char *bitmap = bitmapPtr.get(); - std::fill(&bitmap[0], &bitmap[XRES * YRES], 0); - - if (cm==-1) - { - //if initial flood point is out of bounds, do nothing - if (c != 0 && (x < CELL || x >= XRES-CELL || y < CELL || y >= YRES-CELL || c == PT_SPRK)) - return 1; - else if (x < 0 || x >= XRES || y < 0 || y >= YRES) - return 1; - - if (c == 0) - { - cm = TYP(pmap[y][x]); - if (!cm) - { - cm = TYP(photons[y][x]); - if (!cm) - { - if (bmap[y/CELL][x/CELL]) - return FloodWalls(x, y, WL_ERASE, -1); - else - return -1; - } - } - } - else - cm = 0; - } - - if (c != 0 && IsWallBlocking(x, y, c)) - return 1; - - if (!FloodFillPmapCheck(x, y, cm)) - return 1; - - coord_stack = (short unsigned int (*)[2])malloc(sizeof(unsigned short)*2*coord_stack_limit); - coord_stack[coord_stack_size][0] = x; - coord_stack[coord_stack_size][1] = y; - coord_stack_size++; - - do - { - coord_stack_size--; - x = coord_stack[coord_stack_size][0]; - y = coord_stack[coord_stack_size][1]; - x1 = x2 = x; - // go left as far as possible - while (c?x1>CELL:x1>0) - { - if (bitmap[(y * XRES) + x1 - 1] || !FloodFillPmapCheck(x1-1, y, cm) || (c != 0 && IsWallBlocking(x1-1, y, c))) - { - break; - } - x1--; - } - // go right as far as possible - while (c?x2=CELL+dy:y>=dy) - for (x=x1; x<=x2; x++) - if (!bitmap[((y - dy) * XRES) + x] && FloodFillPmapCheck(x, y-dy, cm) && (c == 0 || !IsWallBlocking(x, y-dy, c))) - { - coord_stack[coord_stack_size][0] = x; - coord_stack[coord_stack_size][1] = y-dy; - coord_stack_size++; - if (coord_stack_size>=coord_stack_limit) - { - free(coord_stack); - return -1; - } - } - - if (c?y=coord_stack_limit) - { - free(coord_stack); - return -1; - } - } - } while (coord_stack_size>0); - free(coord_stack); - return created_something; -} -#endif - void Simulation::orbitalparts_get(int block1, int block2, int resblock1[], int resblock2[]) { resblock1[0] = (block1&0x000000FF); @@ -2205,7 +927,7 @@ int Simulation::get_wavelength_bin(int *wm) if (wM - w0 < 5) return wM + w0; - r = RNG::Ref().gen(); + r = rng.gen(); i = (r >> 1) % (wM-w0-4); i += w0; @@ -2236,7 +958,7 @@ void Simulation::set_emap(int x, int y) break; x1--; } - while (x2=YRES/CELL-1 || + if (x==x1 || x==x2 || y>=YCELLS-1 || is_wire(x-1, y-1) || is_wire(x+1, y-1) || is_wire(x-1, y+1) || !is_wire(x, y+1) || is_wire(x+1, y+1)) set_emap(x, y-1); } - if (yClear(); if(air) @@ -2657,7 +1346,7 @@ int Simulation::try_move(int i, int x, int y, int nx, int ny) e = eval_move(parts[i].type, nx, ny, &r); /* half-silvered mirror */ - if (!e && parts[i].type==PT_PHOT && ((TYP(r)==PT_BMTL && RNG::Ref().chance(1, 2)) || TYP(pmap[y][x])==PT_BMTL)) + if (!e && parts[i].type==PT_PHOT && ((TYP(r)==PT_BMTL && rng.chance(1, 2)) || TYP(pmap[y][x])==PT_BMTL)) e = 2; if (!e) //if no movement @@ -2707,7 +1396,7 @@ int Simulation::try_move(int i, int x, int y, int nx, int ny) return 0; } - int Element_FILT_interactWavelengths(Particle* cpart, int origWl); + int Element_FILT_interactWavelengths(Simulation *sim, Particle* cpart, int origWl); if (e == 2) //if occupy same space { switch (parts[i].type) @@ -2717,14 +1406,14 @@ int Simulation::try_move(int i, int x, int y, int nx, int ny) switch (TYP(r)) { case PT_GLOW: - if (!parts[ID(r)].life && RNG::Ref().chance(29, 30)) + if (!parts[ID(r)].life && rng.chance(29, 30)) { parts[ID(r)].life = 120; create_gain_photon(i); } break; case PT_FILT: - parts[i].ctype = Element_FILT_interactWavelengths(&parts[ID(r)], parts[i].ctype); + parts[i].ctype = Element_FILT_interactWavelengths(this, &parts[ID(r)], parts[i].ctype); break; case PT_C5: if (parts[ID(r)].life > 0 && (parts[ID(r)].ctype & parts[i].ctype & 0xFFFFFFC0)) @@ -2807,7 +1496,7 @@ int Simulation::try_move(int i, int x, int y, int nx, int ny) } case PT_NEUT: if (TYP(r) == PT_GLAS || TYP(r) == PT_BGLA) - if (RNG::Ref().chance(9, 10)) + if (rng.chance(9, 10)) create_cherenkov_photon(i); break; case PT_ELEC: @@ -2824,7 +1513,7 @@ int Simulation::try_move(int i, int x, int y, int nx, int ny) case PT_BIZR: case PT_BIZRG: if (TYP(r) == PT_FILT) - parts[i].ctype = Element_FILT_interactWavelengths(&parts[ID(r)], parts[i].ctype); + parts[i].ctype = Element_FILT_interactWavelengths(this, &parts[ID(r)], parts[i].ctype); break; } return 1; @@ -2884,7 +1573,7 @@ int Simulation::try_move(int i, int x, int y, int nx, int ny) case PT_SOAP: if (parts[i].type == PT_OIL) { - if (RNG::Ref().chance(19, 20) || std::abs(parts[i].x - nx) > 3 || std::abs(parts[i].y - ny) > 3) + if (rng.chance(19, 20) || std::abs(parts[i].x - nx) > 3 || std::abs(parts[i].y - ny) > 3) return 0; } break; @@ -2990,30 +1679,39 @@ int Simulation::do_move(int i, int x, int y, float nxf, float nyf) result = try_move(i, x, y, nx, ny); if (result) { - int t = parts[i].type; - parts[i].x = nxf; - parts[i].y = nyf; - if (ny!=y || nx!=x) - { - if (ID(pmap[y][x]) == i) - pmap[y][x] = 0; - if (ID(photons[y][x]) == i) - photons[y][x] = 0; - // kill_part if particle is out of bounds - if (nx < CELL || nx >= XRES - CELL || ny < CELL || ny >= YRES - CELL) - { - kill_part(i); - return -1; - } - if (elements[t].Properties & TYPE_ENERGY) - photons[ny][nx] = PMAP(i, t); - else if (t) - pmap[ny][nx] = PMAP(i, t); - } + if (!move(i, x, y, nxf, nyf)) + return -1; } return result; } +bool Simulation::move(int i, int x, int y, float nxf, float nyf) +{ + int nx = (int)(nxf+0.5f), ny = (int)(nyf+0.5f); + int t = parts[i].type; + parts[i].x = nxf; + parts[i].y = nyf; + if (ny != y || nx != x) + { + if (ID(pmap[y][x]) == i) + pmap[y][x] = 0; + if (ID(photons[y][x]) == i) + photons[y][x] = 0; + // kill_part if particle is out of bounds + if (nx < CELL || nx >= XRES - CELL || ny < CELL || ny >= YRES - CELL) + { + kill_part(i); + return false; + } + if (elements[t].Properties & TYPE_ENERGY) + photons[ny][nx] = PMAP(i, t); + else if (t) + pmap[ny][nx] = PMAP(i, t); + } + + return true; +} + void Simulation::photoelectric_effect(int nx, int ny)//create sparks from PHOT when hitting PSCN and NSCN { unsigned r = pmap[ny][nx]; @@ -3030,35 +1728,6 @@ void Simulation::photoelectric_effect(int nx, int ny)//create sparks from PHOT w } } -unsigned Simulation::direction_to_map(float dx, float dy, int t) -{ - // TODO: - // Adding extra directions causes some inaccuracies. - // Not adding them causes problems with some diagonal surfaces (photons absorbed instead of reflected). - // For now, don't add them. - // Solution may involve more intelligent setting of initial i0 value in find_next_boundary? - // or rewriting normal/boundary finding code - - return (dx >= 0) | - (((dx + dy) >= 0) << 1) | /* 567 */ - ((dy >= 0) << 2) | /* 4+0 */ - (((dy - dx) >= 0) << 3) | /* 321 */ - ((dx <= 0) << 4) | - (((dx + dy) <= 0) << 5) | - ((dy <= 0) << 6) | - (((dy - dx) <= 0) << 7); - /* - return (dx >= -0.001) | - (((dx + dy) >= -0.001) << 1) | // 567 - ((dy >= -0.001) << 2) | // 4+0 - (((dy - dx) >= -0.001) << 3) | // 321 - ((dx <= 0.001) << 4) | - (((dx + dy) <= 0.001) << 5) | - ((dy <= 0.001) << 6) | - (((dy - dx) <= 0.001) << 7); - }*/ -} - int Simulation::is_blocking(int t, int x, int y) { if (t & REFRACT) { @@ -3100,7 +1769,7 @@ int Simulation::find_next_boundary(int pt, int *x, int *y, int dm, int *em, bool unsigned int mask = 0; for (int i = 0; i < 8; ++i) { - if ((dm & (1U << i)) && is_blocking(pt, *x + dx[i], *y + dy[i])) + if (is_blocking(pt, *x + dx[i], *y + dy[i])) { mask |= (1U << i); } @@ -3108,7 +1777,7 @@ int Simulation::find_next_boundary(int pt, int *x, int *y, int dm, int *em, bool for (int i = 0; i < 8; ++i) { int n = (i + (reverse ? 1 : -1)) & 7; - if (((mask & (1U << i))) && !(mask & (1U << n))) + if (((dm & mask & (1U << i))) && !(mask & (1U << n))) { *x += dx[i]; *y += dy[i]; @@ -3133,8 +1802,8 @@ int Simulation::get_normal(int pt, int x, int y, float dx, float dy, float *nx, if (!is_boundary(pt, x, y)) return 0; - ldm = direction_to_map(-dy, dx, pt); - rdm = direction_to_map(dy, -dx, pt); + ldm = 0xFF; + rdm = 0xFF; lx = rx = x; ly = ry = y; lv = rv = 1; @@ -3388,13 +2057,15 @@ int Simulation::create_part(int p, int x, int y, int t, int v) if((elements[t].Properties & TYPE_PART) && pretty_powder) { int colr, colg, colb; - colr = PIXR(elements[t].Colour) + int(sandcolour * 1.3) + RNG::Ref().between(-20, 20) + RNG::Ref().between(-15, 15); - colg = PIXG(elements[t].Colour) + int(sandcolour * 1.3) + RNG::Ref().between(-20, 20) + RNG::Ref().between(-15, 15); - colb = PIXB(elements[t].Colour) + int(sandcolour * 1.3) + RNG::Ref().between(-20, 20) + RNG::Ref().between(-15, 15); - colr = colr>255 ? 255 : (colr<0 ? 0 : colr); - colg = colg>255 ? 255 : (colg<0 ? 0 : colg); - colb = colb>255 ? 255 : (colb<0 ? 0 : colb); - parts[i].dcolour = (RNG::Ref().between(0, 149)<<24) | (colr<<16) | (colg<<8) | colb; + int sandcolourToUse = p == -2 ? sandcolour_interface : sandcolour; + RGB colour = elements[t].Colour; + colr = colour.Red + int(sandcolourToUse * 1.3) + rng.between(-20, 20) + rng.between(-15, 15); + colg = colour.Green + int(sandcolourToUse * 1.3) + rng.between(-20, 20) + rng.between(-15, 15); + colb = colour.Blue + int(sandcolourToUse * 1.3) + rng.between(-20, 20) + rng.between(-15, 15); + colr = std::clamp(colr, 0, 255); + colg = std::clamp(colg, 0, 255); + colb = std::clamp(colb, 0, 255); + parts[i].dcolour = (rng.between(0, 149)<<24) | (colr<<16) | (colg<<8) | colb; } // Set non-static properties (such as randomly generated ones) @@ -3442,8 +2113,8 @@ void Simulation::GetGravityField(int x, int y, float particleGrav, float newtonG } if (newtonGrav) { - pGravX += newtonGrav*gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]; - pGravY += newtonGrav*gravy[(y/CELL)*(XRES/CELL)+(x/CELL)]; + pGravX += newtonGrav*gravx[(y/CELL)*XCELLS+(x/CELL)]; + pGravY += newtonGrav*gravy[(y/CELL)*XCELLS+(x/CELL)]; } } @@ -3456,7 +2127,7 @@ void Simulation::create_gain_photon(int pp)//photons from PHOT going through GLO return; i = pfree; - lr = 2*RNG::Ref().between(0, 1) - 1; // -1 or 1 + lr = 2*rng.between(0, 1) - 1; // -1 or 1 xx = parts[pp].x - lr*0.3*parts[pp].vy; yy = parts[pp].y + lr*0.3*parts[pp].vx; @@ -3511,7 +2182,7 @@ void Simulation::create_cherenkov_photon(int pp)//photons from NEUT going throug pfree = parts[i].life; if (i>parts_lastActiveIndex) parts_lastActiveIndex = i; - lr = RNG::Ref().between(0, 1); + lr = rng.between(0, 1); parts[i].type = PT_PHOT; parts[i].ctype = 0x00000F80; @@ -3554,7 +2225,7 @@ void Simulation::delete_part(int x, int y)//calls kill_part with the particle lo void Simulation::UpdateParticles(int start, int end) { - int i, j, x, y, t, nx, ny, r, surround_space, s, rt, nt; + int i, j, x, y, t, r, surround_space, s, rt, nt; float mv, dx, dy, nrx, nry, dp, ctemph, ctempl, gravtot; int fin_x, fin_y, clear_x, clear_y, stagnant; float fin_xf, fin_yf, clear_xf, clear_yf; @@ -3567,7 +2238,7 @@ void Simulation::UpdateParticles(int start, int end) bool transitionOccurred; //the main particle loop function, goes over all particles. - for (i = start; i <= end && i <= parts_lastActiveIndex; i++) + for (i = start; i < end && i <= parts_lastActiveIndex; i++) if (parts[i].type) { debug_mostRecentlyUpdated = i; @@ -3662,19 +2333,19 @@ void Simulation::UpdateParticles(int start, int end) { #ifdef REALISTIC //The magic number controls diffusion speed - parts[i].vx += 0.05*sqrtf(parts[i].temp)*elements[t].Diffusion*(2.0f*RNG::Ref().uniform01()-1.0f); - parts[i].vy += 0.05*sqrtf(parts[i].temp)*elements[t].Diffusion*(2.0f*RNG::Ref().uniform01()-1.0f); + parts[i].vx += 0.05*sqrtf(parts[i].temp)*elements[t].Diffusion*(2.0f*rng.uniform01()-1.0f); + parts[i].vy += 0.05*sqrtf(parts[i].temp)*elements[t].Diffusion*(2.0f*rng.uniform01()-1.0f); #else - parts[i].vx += elements[t].Diffusion*(2.0f*RNG::Ref().uniform01()-1.0f); - parts[i].vy += elements[t].Diffusion*(2.0f*RNG::Ref().uniform01()-1.0f); + parts[i].vx += elements[t].Diffusion*(2.0f*rng.uniform01()-1.0f); + parts[i].vy += elements[t].Diffusion*(2.0f*rng.uniform01()-1.0f); #endif } transitionOccurred = false; j = surround_space = nt = 0;//if nt is greater than 1 after this, then there is a particle around the current particle, that is NOT the current particle's type, for water movement. - for (nx=-1; nx<2; nx++) - for (ny=-1; ny<2; ny++) { + for (auto nx=-1; nx<2; nx++) + for (auto ny=-1; ny<2; ny++) { if (nx||ny) { surround[j] = r = pmap[y+ny][x+nx]; j++; @@ -3689,7 +2360,7 @@ void Simulation::UpdateParticles(int start, int end) if (!legacy_enable) { - if ((elements[t].Properties&TYPE_LIQUID) && (t!=PT_GEL || gel_scale > (1 + RNG::Ref().between(0, 254)))) + if ((elements[t].Properties&TYPE_LIQUID) && (t!=PT_GEL || gel_scale > (1 + rng.between(0, 254)))) { float convGravX, convGravY; GetGravityField(x, y, -2.0f, -2.0f, convGravX, convGravY); @@ -3712,7 +2383,7 @@ void Simulation::UpdateParticles(int start, int end) #ifdef REALISTIC if (t&&(t!=PT_HSWC||parts[i].life==10)&&(elements[t].HeatConduct*gel_scale)) #else - if (t && (t!=PT_HSWC||parts[i].life==10) && RNG::Ref().chance(int(elements[t].HeatConduct*gel_scale), 250)) + if (t && (t!=PT_HSWC||parts[i].life==10) && rng.chance(int(elements[t].HeatConduct*gel_scale), 250)) #endif { if (aheat_enable && !(elements[t].Properties&PROP_NOAMBHEAT)) @@ -3869,7 +2540,7 @@ void Simulation::UpdateParticles(int start, int end) { pt = (c_heat - platent[t])/c_Cm; - t = RNG::Ref().chance(1, 4) ? PT_SALT : PT_WTRV; + t = rng.chance(1, 4) ? PT_SALT : PT_WTRV; } else { @@ -3877,7 +2548,7 @@ void Simulation::UpdateParticles(int start, int end) s = 0; } #else - t = RNG::Ref().chance(1, 4) ? PT_SALT : PT_WTRV; + t = rng.chance(1, 4) ? PT_SALT : PT_WTRV; #endif } else if (t == PT_BRMT) @@ -4023,7 +2694,7 @@ void Simulation::UpdateParticles(int start, int end) goto killed; if (t==PT_FIRE || t==PT_PLSM || t==PT_CFLM) - parts[i].life = RNG::Ref().between(120, 169); + parts[i].life = rng.between(120, 169); if (t == PT_LAVA) { if (parts[i].ctype == PT_BRMT) parts[i].ctype = PT_BMTL; @@ -4031,7 +2702,7 @@ void Simulation::UpdateParticles(int start, int end) else if (parts[i].ctype == PT_BGLA) parts[i].ctype = PT_GLAS; else if (parts[i].ctype == PT_PQRT) parts[i].ctype = PT_QRTZ; else if (parts[i].ctype == PT_LITH && parts[i].tmp2 > 3) parts[i].ctype = PT_GLAS; - parts[i].life = RNG::Ref().between(240, 359); + parts[i].life = rng.between(240, 359); } transitionOccurred = true; } @@ -4071,21 +2742,21 @@ void Simulation::UpdateParticles(int start, int end) //spark updates from walls if ((elements[t].Properties&PROP_CONDUCTS) || t==PT_SPRK) { - nx = x % CELL; + auto nx = x % CELL; if (nx == 0) nx = x/CELL - 1; else if (nx == CELL-1) nx = x/CELL + 1; else nx = x/CELL; - ny = y % CELL; + auto ny = y % CELL; if (ny == 0) ny = y/CELL - 1; else if (ny == CELL-1) ny = y/CELL + 1; else ny = y/CELL; - if (nx>=0 && ny>=0 && nx=0 && ny>=0 && nx2.5f) { - parts[i].life = RNG::Ref().between(180, 259); + parts[i].life = rng.between(180, 259); parts[i].temp = restrict_flt(elements[PT_FIRE].DefaultProperties.temp + (elements[t].Flammable/2), MIN_TEMP, MAX_TEMP); t = PT_FIRE; part_change_type(i,x,y,t); @@ -4114,7 +2785,7 @@ void Simulation::UpdateParticles(int start, int end) s = 1; - gravtot = fabs(gravy[(y/CELL)*(XRES/CELL)+(x/CELL)])+fabs(gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]); + gravtot = fabs(gravy[(y/CELL)*XCELLS+(x/CELL)])+fabs(gravx[(y/CELL)*XCELLS+(x/CELL)]); if (elements[t].HighPressureTransition>-1 && pv[y/CELL][x/CELL]>elements[t].HighPressure) { // particle type change due to high pressure if (elements[t].HighPressureTransition!=PT_NUM) @@ -4161,7 +2832,7 @@ void Simulation::UpdateParticles(int start, int end) if (part_change_type(i,x,y,t)) goto killed; if (t == PT_FIRE) - parts[i].life = RNG::Ref().between(120, 169); + parts[i].life = rng.between(120, 169); transitionOccurred = true; } @@ -4369,10 +3040,10 @@ killed: } nn = GLASS_IOR - GLASS_DISP*(r-30)/30.0f; nn *= nn; - nrx = -nrx; - nry = -nry; - if (rt_glas && !lt_glas) - nn = 1.0f/nn; + auto enter = rt_glas && !lt_glas; + nrx = enter ? -nrx : nrx; + nry = enter ? -nry : nry; + nn = enter ? 1.0f/nn : nn; ct1 = parts[i].vx*nrx + parts[i].vy*nry; ct2 = 1.0f - (nn*nn)*(1.0f-(ct1*ct1)); if (ct2 < 0.0f) { @@ -4407,7 +3078,7 @@ killed: continue; // reflection parts[i].flags |= FLAG_STAGNANT; - if (t==PT_NEUT && RNG::Ref().chance(1, 10)) + if (t==PT_NEUT && rng.chance(1, 10)) { kill_part(i); continue; @@ -4523,7 +3194,7 @@ killed: { if (TYP(r) == PT_CRMC) { - float r = RNG::Ref().between(-50, 50) * 0.01f, rx, ry, anrx, anry; + float r = rng.between(-50, 50) * 0.01f, rx, ry, anrx, anry; r = r * r * r; rx = cosf(r); ry = sinf(r); anrx = rx * nrx + ry * nry; @@ -4585,7 +3256,7 @@ killed: else { // Checking stagnant is cool, but then it doesn't update when you change it later. - if (water_equal_test && elements[t].Falldown == 2 && RNG::Ref().chance(1, 200)) + if (water_equal_test && elements[t].Falldown == 2 && rng.chance(1, 200)) { if (flood_water(x, y, i)) goto movedone; @@ -4608,7 +3279,7 @@ killed: else { s = 1; - r = RNG::Ref().between(0, 1) * 2 - 1;// position search direction (left/right first) + r = rng.between(0, 1) * 2 - 1;// position search direction (left/right first) if ((clear_x!=x || clear_y!=y || nt || surround_space) && (fabsf(parts[i].vx)>0.01f || fabsf(parts[i].vy)>0.01f)) { @@ -4648,6 +3319,7 @@ killed: if (t==PT_GEL) rt = int(parts[i].tmp*0.20f+5.0f); + auto nx = -1, ny = -1; for (j=clear_x+r; j>=0 && j>=clear_x-rt && j1500 || (unsigned int)RNG::Ref().between(0, 1599) <= (pmap_count[y][x]+100)) + else if (pmap_count[y][x]>1500 || (unsigned int)rng.between(0, 1599) <= (pmap_count[y][x]+100)) { pmap_count[y][x] = pmap_count[y][x] + NPART; excessive_stacking_found = true; @@ -5156,10 +3807,6 @@ void Simulation::BeforeSim() { if (!sys_pause||framerender) { -#ifdef LUACONSOLE - luacon_ci->HandleEvent(LuaEvents::beforesim, new BeforeSimEvent()); -#endif - air->update_air(); if(aheat_enable) @@ -5170,10 +3817,10 @@ void Simulation::BeforeSim() grav->gravity_update_async(); //Get updated buffer pointers for gravity - gravx = grav->gravx; - gravy = grav->gravy; - gravp = grav->gravp; - gravmap = grav->gravmap; + gravx = &grav->gravx[0]; + gravy = &grav->gravy[0]; + gravp = &grav->gravp[0]; + gravmap = &grav->gravmap[0]; } if(emp_decor>0) emp_decor -= emp_decor/25+2; @@ -5188,8 +3835,9 @@ void Simulation::BeforeSim() if (elementRecount) std::fill(elementCount, elementCount+PT_NUM, 0); } - sandcolour = (int)(20.0f*sin((float)sandcolour_frame*(M_PI/180.0f))); + sandcolour_interface = (int)(20.0f*sin((float)sandcolour_frame*(TPT_PI_FLT/180.0f))); sandcolour_frame = (sandcolour_frame+1)%360; + sandcolour = (int)(20.0f*sin((float)(frameCount)*(TPT_PI_FLT/180.0f))); if (gravWallChanged) { @@ -5197,16 +3845,16 @@ void Simulation::BeforeSim() gravWallChanged = false; } - if (debug_currentParticle == 0) + if (debug_nextToUpdate == 0) RecalcFreeParticles(true); if (!sys_pause || framerender) { // decrease wall conduction, make walls block air and ambient heat int x, y; - for (y = 0; y < YRES/CELL; y++) + for (y = 0; y < YCELLS; y++) { - for (x = 0; x < XRES/CELL; x++) + for (x = 0; x < XCELLS; x++) { if (emap[y][x]) emap[y][x] --; @@ -5216,7 +3864,7 @@ void Simulation::BeforeSim() } // check for stacking and create BHOL if found - if (force_stacking_check || RNG::Ref().chance(1, 10)) + if (force_stacking_check || rng.chance(1, 10)) { CheckStacking(); } @@ -5361,21 +4009,18 @@ void Simulation::AfterSim() emp_trigger_count = 0; } -#ifdef LUACONSOLE - luacon_ci->HandleEvent(LuaEvents::aftersim, new AfterSimEvent()); -#endif + frameCount += 1; } Simulation::~Simulation() { - delete grav; delete air; } Simulation::Simulation(): replaceModeSelected(0), replaceModeFlags(0), - debug_currentParticle(0), + debug_nextToUpdate(0), ISWIRE(0), force_stacking_check(false), emp_decor(0), @@ -5410,14 +4055,14 @@ Simulation::Simulation(): elementRecount = true; //Create and attach gravity simulation - grav = new Gravity(); + grav = Gravity::Create(); //Give air sim references to our data grav->bmap = bmap; //Gravity sim gives us maps to use - gravx = grav->gravx; - gravy = grav->gravy; - gravp = grav->gravp; - gravmap = grav->gravmap; + gravx = &grav->gravx[0]; + gravy = &grav->gravy[0]; + gravp = &grav->gravp[0]; + gravmap = &grav->gravmap[0]; //Create and attach air simulation air = new Air(*this); @@ -5514,11 +4159,6 @@ String Simulation::BasicParticleInfo(Particle const &sample_part) const return sampleInfo.Build(); } -bool Simulation::InBounds(int x, int y) -{ - return (x>=0 && y>=0 && x=0 ? 0 : y); diff --git a/src/simulation/Simulation.h b/src/simulation/Simulation.h index 7c9cad20f..cd2df682b 100644 --- a/src/simulation/Simulation.h +++ b/src/simulation/Simulation.h @@ -1,13 +1,4 @@ -#ifndef SIMULATION_H -#define SIMULATION_H -#include "Config.h" - -#include -#include -#include -#include -#include - +#pragma once #include "Particle.h" #include "Stickman.h" #include "WallType.h" @@ -16,10 +7,18 @@ #include "BuiltinGOL.h" #include "MenuSection.h" #include "CoordStack.h" - +#include "gravity/GravityPtr.h" +#include "common/tpt-rand.h" #include "Element.h" +#include "SimulationConfig.h" +#include +#include +#include +#include +#include +#include -#define CHANNELS ((int)(MAX_TEMP-73)/100+2) +constexpr int CHANNELS = int(MAX_TEMP - 73) / 100 + 2; class Snapshot; class SimTool; @@ -38,8 +37,9 @@ class Simulation { public: - Gravity * grav; + GravityPtr grav; Air * air; + RNG rng; std::vector signs; std::array elements; @@ -54,7 +54,7 @@ public: int replaceModeFlags; char can_move[PT_NUM][PT_NUM]; - int debug_currentParticle; + int debug_nextToUpdate; int debug_mostRecentlyUpdated = -1; // -1 when between full update loops int parts_lastActiveIndex; int pfree; @@ -84,20 +84,20 @@ public: int GSPEED; unsigned int gol[YRES][XRES][5]; //Air sim - float (*vx)[XRES/CELL]; - float (*vy)[XRES/CELL]; - float (*pv)[XRES/CELL]; - float (*hv)[XRES/CELL]; + float (*vx)[XCELLS]; + float (*vy)[XCELLS]; + float (*pv)[XCELLS]; + float (*hv)[XCELLS]; //Gravity sim - float *gravx;//gravx[(YRES/CELL) * (XRES/CELL)]; - float *gravy;//gravy[(YRES/CELL) * (XRES/CELL)]; - float *gravp;//gravp[(YRES/CELL) * (XRES/CELL)]; - float *gravmap;//gravmap[(YRES/CELL) * (XRES/CELL)]; + float *gravx;//gravx[YCELLS * XCELLS]; + float *gravy;//gravy[YCELLS * XCELLS]; + float *gravp;//gravp[YCELLS * XCELLS]; + float *gravmap;//gravmap[YCELLS * XCELLS]; //Walls - unsigned char bmap[YRES/CELL][XRES/CELL]; - unsigned char emap[YRES/CELL][XRES/CELL]; - float fvx[YRES/CELL][XRES/CELL]; - float fvy[YRES/CELL][XRES/CELL]; + unsigned char bmap[YCELLS][XCELLS]; + unsigned char emap[YCELLS][XCELLS]; + float fvx[YCELLS][XCELLS]; + float fvy[YCELLS][XCELLS]; //Particles Particle parts[NPART]; int pmap[YRES][XRES]; @@ -115,14 +115,15 @@ public: int framerender; int pretty_powder; int sandcolour; + int sandcolour_interface; int sandcolour_frame; int deco_space; + uint64_t frameCount; + bool ensureDeterminism; - int Load(const GameSave * save, bool includePressure); - int Load(const GameSave * save, bool includePressure, int x, int y); - GameSave * Save(bool includePressure); - GameSave * Save(bool includePressure, int x1, int y1, int x2, int y2); - void SaveSimOptions(GameSave * gameSave); + void Load(const GameSave *save, bool includePressure, Vec2 blockP); // block coordinates + std::unique_ptr Save(bool includePressure, Rect partR); // particle coordinates + void SaveSimOptions(GameSave &gameSave); SimulationSample GetSample(int x, int y); std::unique_ptr CreateSnapshot(); @@ -132,8 +133,8 @@ public: int is_boundary(int pt, int x, int y); int find_next_boundary(int pt, int *x, int *y, int dm, int *em, bool reverse); void photoelectric_effect(int nx, int ny); - unsigned direction_to_map(float dx, float dy, int t); int do_move(int i, int x, int y, float nxf, float nyf); + bool move(int i, int x, int y, float nxf, float nyf); int try_move(int i, int x, int y, int nx, int ny); int eval_move(int pt, int nx, int ny, unsigned *rr); void init_can_move(); @@ -162,8 +163,7 @@ public: int is_wire_off(int x, int y); void set_emap(int x, int y); int parts_avg(int ci, int ni, int t); - void create_arc(int sx, int sy, int dx, int dy, int midpoints, int variance, int type, int flags); - void UpdateParticles(int start, int end); // range inclusive on both ends + void UpdateParticles(int start, int end); // Dispatches an update to the range [start, end). void SimulateGoL(); void RecalcFreeParticles(bool do_life_dec); void CheckStacking(); @@ -177,29 +177,29 @@ public: //Drawing Deco void ApplyDecoration(int x, int y, int colR, int colG, int colB, int colA, int mode); - void ApplyDecorationPoint(int x, int y, int colR, int colG, int colB, int colA, int mode, Brush * cBrush = NULL); - void ApplyDecorationLine(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode, Brush * cBrush = NULL); + void ApplyDecorationPoint(int x, int y, int colR, int colG, int colB, int colA, int mode, Brush const &cBrush); + void ApplyDecorationLine(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode, Brush const &cBrush); void ApplyDecorationBox(int x1, int y1, int x2, int y2, int colR, int colG, int colB, int colA, int mode); bool ColorCompare(Renderer *ren, int x, int y, int replaceR, int replaceG, int replaceB); void ApplyDecorationFill(Renderer *ren, int x, int y, int colR, int colG, int colB, int colA, int replaceR, int replaceG, int replaceB); //Drawing Tools like HEAT, AIR, and GRAV int Tool(int x, int y, int tool, int brushX, int brushY, float strength = 1.0f); - int ToolBrush(int x, int y, int tool, Brush * cBrush, float strength = 1.0f); - void ToolLine(int x1, int y1, int x2, int y2, int tool, Brush * cBrush, float strength = 1.0f); + int ToolBrush(int x, int y, int tool, Brush const &cBrush, float strength = 1.0f); + void ToolLine(int x1, int y1, int x2, int y2, int tool, Brush const &cBrush, float strength = 1.0f); void ToolBox(int x1, int y1, int x2, int y2, int tool, float strength = 1.0f); //Drawing Walls - int CreateWalls(int x, int y, int rx, int ry, int wall, Brush * cBrush = NULL); - void CreateWallLine(int x1, int y1, int x2, int y2, int rx, int ry, int wall, Brush * cBrush = NULL); + int CreateWalls(int x, int y, int rx, int ry, int wall, Brush const *cBrush = nullptr); + void CreateWallLine(int x1, int y1, int x2, int y2, int rx, int ry, int wall, Brush const *cBrush = nullptr); void CreateWallBox(int x1, int y1, int x2, int y2, int wall); int FloodWalls(int x, int y, int wall, int bm); //Drawing Particles - int CreateParts(int positionX, int positionY, int c, Brush * cBrush, int flags = -1); + int CreateParts(int positionX, int positionY, int c, Brush const &cBrush, int flags = -1); int CreateParts(int x, int y, int rx, int ry, int c, int flags = -1); int CreatePartFlags(int x, int y, int c, int flags); - void CreateLine(int x1, int y1, int x2, int y2, int c, Brush * cBrush, int flags = -1); + void CreateLine(int x1, int y1, int x2, int y2, int c, Brush const &cBrush, int flags = -1); void CreateLine(int x1, int y1, int x2, int y2, int c); void CreateBox(int x1, int y1, int x2, int y2, int c, int flags = -1); int FloodParts(int x, int y, int c, int cm, int flags = -1); @@ -218,7 +218,10 @@ public: Simulation(); ~Simulation(); - bool InBounds(int x, int y); + static bool InBounds(int x, int y) + { + return RES.OriginRect().Contains({ x, y }); + } // These don't really belong anywhere at the moment, so go here for loop edge mode static int remainder_p(int x, int y); @@ -250,5 +253,3 @@ public: private: CoordStack& getCoordStackSingleton(); }; - -#endif /* SIMULATION_H */ diff --git a/src/simulation/SimulationData.cpp b/src/simulation/SimulationData.cpp index c646e9070..db996a175 100644 --- a/src/simulation/SimulationData.cpp +++ b/src/simulation/SimulationData.cpp @@ -21,55 +21,55 @@ const BuiltinGOL builtinGol[NGOL] = { // * the ruleset constants below look 20-bit, but rulesets actually consist of 21 // bits of data; bit 20 just happens to not be set for any of the built-in types, // as none of them have 10 or more states - { "GOL", GT_GOL , 0x0080C, PIXPACK(0x0CAC00), PIXPACK(0x0CAC00), NGT_GOL, String("Game Of Life: Begin 3/Stay 23") }, - { "HLIF", GT_HLIF, 0x0480C, PIXPACK(0xFF0000), PIXPACK(0xFF0000), NGT_HLIF, String("High Life: B36/S23") }, - { "ASIM", GT_ASIM, 0x038F0, PIXPACK(0x0000FF), PIXPACK(0x0000FF), NGT_ASIM, String("Assimilation: B345/S4567") }, - { "2X2", GT_2x2 , 0x04826, PIXPACK(0xFFFF00), PIXPACK(0xFFFF00), NGT_2x2, String("2X2: B36/S125") }, - { "DANI", GT_DANI, 0x1C9D8, PIXPACK(0x00FFFF), PIXPACK(0x00FFFF), NGT_DANI, String("Day and Night: B3678/S34678") }, - { "AMOE", GT_AMOE, 0x0A92A, PIXPACK(0xFF00FF), PIXPACK(0xFF00FF), NGT_AMOE, String("Amoeba: B357/S1358") }, - { "MOVE", GT_MOVE, 0x14834, PIXPACK(0xFFFFFF), PIXPACK(0xFFFFFF), NGT_MOVE, String("'Move' particles. Does not move things.. it is a life type: B368/S245") }, - { "PGOL", GT_PGOL, 0x0A90C, PIXPACK(0xE05010), PIXPACK(0xE05010), NGT_PGOL, String("Pseudo Life: B357/S238") }, - { "DMOE", GT_DMOE, 0x1E9E0, PIXPACK(0x500000), PIXPACK(0x500000), NGT_DMOE, String("Diamoeba: B35678/S5678") }, - { "3-4", GT_34 , 0x01818, PIXPACK(0x500050), PIXPACK(0x500050), NGT_34, String("3-4: B34/S34") }, - { "LLIF", GT_LLIF, 0x03820, PIXPACK(0x505050), PIXPACK(0x505050), NGT_LLIF, String("Long Life: B345/S5") }, - { "STAN", GT_STAN, 0x1C9EC, PIXPACK(0x5000FF), PIXPACK(0x5000FF), NGT_STAN, String("Stains: B3678/S235678") }, - { "SEED", GT_SEED, 0x00400, PIXPACK(0xFBEC7D), PIXPACK(0xFBEC7D), NGT_SEED, String("Seeds: B2/S") }, - { "MAZE", GT_MAZE, 0x0083E, PIXPACK(0xA8E4A0), PIXPACK(0xA8E4A0), NGT_MAZE, String("Maze: B3/S12345") }, - { "COAG", GT_COAG, 0x189EC, PIXPACK(0x9ACD32), PIXPACK(0x9ACD32), NGT_COAG, String("Coagulations: B378/S235678") }, - { "WALL", GT_WALL, 0x1F03C, PIXPACK(0x0047AB), PIXPACK(0x0047AB), NGT_WALL, String("Walled cities: B45678/S2345") }, - { "GNAR", GT_GNAR, 0x00202, PIXPACK(0xE5B73B), PIXPACK(0xE5B73B), NGT_GNAR, String("Gnarl: B1/S1") }, - { "REPL", GT_REPL, 0x0AAAA, PIXPACK(0x259588), PIXPACK(0x259588), NGT_REPL, String("Replicator: B1357/S1357") }, - { "MYST", GT_MYST, 0x139E1, PIXPACK(0x0C3C00), PIXPACK(0x0C3C00), NGT_MYST, String("Mystery: B3458/S05678") }, - { "LOTE", GT_LOTE, 0x48938, PIXPACK(0xFF0000), PIXPACK(0xFFFF00), NGT_LOTE, String("Living on the Edge: B37/S3458/4") }, - { "FRG2", GT_FRG2, 0x20816, PIXPACK(0x006432), PIXPACK(0x00FF5A), NGT_FRG2, String("Like Frogs rule: B3/S124/3") }, - { "STAR", GT_STAR, 0x98478, PIXPACK(0x000040), PIXPACK(0x0000E6), NGT_STAR, String("Like Star Wars rule: B278/S3456/6") }, - { "FROG", GT_FROG, 0x21806, PIXPACK(0x006400), PIXPACK(0x00FF00), NGT_FROG, String("Frogs: B34/S12/3") }, - { "BRAN", GT_BRAN, 0x25440, PIXPACK(0xFFFF00), PIXPACK(0x969600), NGT_BRAN, String("Brian 6: B246/S6/3" )} + { "GOL", GT_GOL , 0x0080C, 0x0CAC00_rgb, 0x0CAC00_rgb, NGT_GOL, String("Game Of Life: Begin 3/Stay 23") }, + { "HLIF", GT_HLIF, 0x0480C, 0xFF0000_rgb, 0xFF0000_rgb, NGT_HLIF, String("High Life: B36/S23") }, + { "ASIM", GT_ASIM, 0x038F0, 0x0000FF_rgb, 0x0000FF_rgb, NGT_ASIM, String("Assimilation: B345/S4567") }, + { "2X2", GT_2x2 , 0x04826, 0xFFFF00_rgb, 0xFFFF00_rgb, NGT_2x2, String("2X2: B36/S125") }, + { "DANI", GT_DANI, 0x1C9D8, 0x00FFFF_rgb, 0x00FFFF_rgb, NGT_DANI, String("Day and Night: B3678/S34678") }, + { "AMOE", GT_AMOE, 0x0A92A, 0xFF00FF_rgb, 0xFF00FF_rgb, NGT_AMOE, String("Amoeba: B357/S1358") }, + { "MOVE", GT_MOVE, 0x14834, 0xFFFFFF_rgb, 0xFFFFFF_rgb, NGT_MOVE, String("'Move' particles. Does not move things.. it is a life type: B368/S245") }, + { "PGOL", GT_PGOL, 0x0A90C, 0xE05010_rgb, 0xE05010_rgb, NGT_PGOL, String("Pseudo Life: B357/S238") }, + { "DMOE", GT_DMOE, 0x1E9E0, 0x500000_rgb, 0x500000_rgb, NGT_DMOE, String("Diamoeba: B35678/S5678") }, + { "3-4", GT_34 , 0x01818, 0x500050_rgb, 0x500050_rgb, NGT_34, String("3-4: B34/S34") }, + { "LLIF", GT_LLIF, 0x03820, 0x505050_rgb, 0x505050_rgb, NGT_LLIF, String("Long Life: B345/S5") }, + { "STAN", GT_STAN, 0x1C9EC, 0x5000FF_rgb, 0x5000FF_rgb, NGT_STAN, String("Stains: B3678/S235678") }, + { "SEED", GT_SEED, 0x00400, 0xFBEC7D_rgb, 0xFBEC7D_rgb, NGT_SEED, String("Seeds: B2/S") }, + { "MAZE", GT_MAZE, 0x0083E, 0xA8E4A0_rgb, 0xA8E4A0_rgb, NGT_MAZE, String("Maze: B3/S12345") }, + { "COAG", GT_COAG, 0x189EC, 0x9ACD32_rgb, 0x9ACD32_rgb, NGT_COAG, String("Coagulations: B378/S235678") }, + { "WALL", GT_WALL, 0x1F03C, 0x0047AB_rgb, 0x0047AB_rgb, NGT_WALL, String("Walled cities: B45678/S2345") }, + { "GNAR", GT_GNAR, 0x00202, 0xE5B73B_rgb, 0xE5B73B_rgb, NGT_GNAR, String("Gnarl: B1/S1") }, + { "REPL", GT_REPL, 0x0AAAA, 0x259588_rgb, 0x259588_rgb, NGT_REPL, String("Replicator: B1357/S1357") }, + { "MYST", GT_MYST, 0x139E1, 0x0C3C00_rgb, 0x0C3C00_rgb, NGT_MYST, String("Mystery: B3458/S05678") }, + { "LOTE", GT_LOTE, 0x48938, 0xFF0000_rgb, 0xFFFF00_rgb, NGT_LOTE, String("Living on the Edge: B37/S3458/4") }, + { "FRG2", GT_FRG2, 0x20816, 0x006432_rgb, 0x00FF5A_rgb, NGT_FRG2, String("Like Frogs rule: B3/S124/3") }, + { "STAR", GT_STAR, 0x98478, 0x000040_rgb, 0x0000E6_rgb, NGT_STAR, String("Like Star Wars rule: B278/S3456/6") }, + { "FROG", GT_FROG, 0x21806, 0x006400_rgb, 0x00FF00_rgb, NGT_FROG, String("Frogs: B34/S12/3") }, + { "BRAN", GT_BRAN, 0x25440, 0xFFFF00_rgb, 0x969600_rgb, NGT_BRAN, String("Brian 6: B246/S6/3" )} }; std::vector LoadWalls() { return std::vector{ - {PIXPACK(0x808080), PIXPACK(0x000000), 0, Renderer::WallIcon, String("ERASE"), "DEFAULT_WL_ERASE", String("Erases walls.")}, - {PIXPACK(0xC0C0C0), PIXPACK(0x101010), 0, Renderer::WallIcon, String("CONDUCTIVE WALL"), "DEFAULT_WL_CNDTW", String("Blocks everything. Conductive.")}, - {PIXPACK(0x808080), PIXPACK(0x808080), 0, Renderer::WallIcon, String("EWALL"), "DEFAULT_WL_EWALL", String("E-Wall. Becomes transparent when electricity is connected.")}, - {PIXPACK(0xFF8080), PIXPACK(0xFF2008), 1, Renderer::WallIcon, String("DETECTOR"), "DEFAULT_WL_DTECT", String("Detector. Generates electricity when a particle is inside.")}, - {PIXPACK(0x808080), PIXPACK(0x000000), 0, Renderer::WallIcon, String("STREAMLINE"), "DEFAULT_WL_STRM", String("Streamline. Set start point of a streamline.")}, - {PIXPACK(0x8080FF), PIXPACK(0x000000), 1, Renderer::WallIcon, String("FAN"), "DEFAULT_WL_FAN", String("Fan. Accelerates air. Use the line tool to set direction and strength.")}, - {PIXPACK(0xC0C0C0), PIXPACK(0x101010), 2, Renderer::WallIcon, String("LIQUID WALL"), "DEFAULT_WL_LIQD", String("Allows liquids, blocks all other particles. Conductive.")}, - {PIXPACK(0x808080), PIXPACK(0x000000), 1, Renderer::WallIcon, String("ABSORB WALL"), "DEFAULT_WL_ABSRB", String("Absorbs particles but lets air currents through.")}, - {PIXPACK(0x808080), PIXPACK(0x000000), 3, Renderer::WallIcon, String("WALL"), "DEFAULT_WL_WALL", String("Basic wall, blocks everything.")}, - {PIXPACK(0x3C3C3C), PIXPACK(0x000000), 1, Renderer::WallIcon, String("AIRONLY WALL"), "DEFAULT_WL_AIR", String("Allows air, but blocks all particles.")}, - {PIXPACK(0x575757), PIXPACK(0x000000), 1, Renderer::WallIcon, String("POWDER WALL"), "DEFAULT_WL_POWDR", String("Allows powders, blocks all other particles.")}, - {PIXPACK(0xFFFF22), PIXPACK(0x101010), 2, Renderer::WallIcon, String("CONDUCTOR"), "DEFAULT_WL_CNDTR", String("Conductor. Allows all particles to pass through and conducts electricity.")}, - {PIXPACK(0x242424), PIXPACK(0x101010), 0, Renderer::WallIcon, String("EHOLE"), "DEFAULT_WL_EHOLE", String("E-Hole. absorbs particles, releases them when powered.")}, - {PIXPACK(0x579777), PIXPACK(0x000000), 1, Renderer::WallIcon, String("GAS WALL"), "DEFAULT_WL_GAS", String("Allows gases, blocks all other particles.")}, - {PIXPACK(0xFFEE00), PIXPACK(0xAA9900), 4, Renderer::WallIcon, String("GRAVITY WALL"), "DEFAULT_WL_GRVTY", String("Gravity wall. Newtonian Gravity has no effect inside a box drawn with this.")}, - {PIXPACK(0xFFAA00), PIXPACK(0xAA5500), 4, Renderer::WallIcon, String("ENERGY WALL"), "DEFAULT_WL_ENRGY", String("Allows energy particles, blocks all other particles.")}, - {PIXPACK(0xDCDCDC), PIXPACK(0x000000), 1, Renderer::WallIcon, String("AIRBLOCK WALL"), "DEFAULT_WL_NOAIR", String("Allows all particles, but blocks air.")}, - {PIXPACK(0x808080), PIXPACK(0x000000), 0, Renderer::WallIcon, String("ERASEALL"), "DEFAULT_WL_ERASEA", String("Erases walls, particles, and signs.")}, - {PIXPACK(0x800080), PIXPACK(0x000000), 0, Renderer::WallIcon, String("STASIS WALL"), "DEFAULT_WL_STASIS", String("Freezes particles inside the wall in place until powered.")}, + {0x808080_rgb, 0x000000_rgb, 0, Renderer::WallIcon, String("ERASE"), "DEFAULT_WL_ERASE", String("Erases walls.")}, + {0xC0C0C0_rgb, 0x101010_rgb, 0, Renderer::WallIcon, String("CONDUCTIVE WALL"), "DEFAULT_WL_CNDTW", String("Blocks everything. Conductive.")}, + {0x808080_rgb, 0x808080_rgb, 0, Renderer::WallIcon, String("EWALL"), "DEFAULT_WL_EWALL", String("E-Wall. Becomes transparent when electricity is connected.")}, + {0xFF8080_rgb, 0xFF2008_rgb, 1, Renderer::WallIcon, String("DETECTOR"), "DEFAULT_WL_DTECT", String("Detector. Generates electricity when a particle is inside.")}, + {0x808080_rgb, 0x000000_rgb, 0, Renderer::WallIcon, String("STREAMLINE"), "DEFAULT_WL_STRM", String("Streamline. Set start point of a streamline.")}, + {0x8080FF_rgb, 0x000000_rgb, 1, Renderer::WallIcon, String("FAN"), "DEFAULT_WL_FAN", String("Fan. Accelerates air. Use the line tool to set direction and strength.")}, + {0xC0C0C0_rgb, 0x101010_rgb, 2, Renderer::WallIcon, String("LIQUID WALL"), "DEFAULT_WL_LIQD", String("Allows liquids, blocks all other particles. Conductive.")}, + {0x808080_rgb, 0x000000_rgb, 1, Renderer::WallIcon, String("ABSORB WALL"), "DEFAULT_WL_ABSRB", String("Absorbs particles but lets air currents through.")}, + {0x808080_rgb, 0x000000_rgb, 3, Renderer::WallIcon, String("WALL"), "DEFAULT_WL_WALL", String("Basic wall, blocks everything.")}, + {0x3C3C3C_rgb, 0x000000_rgb, 1, Renderer::WallIcon, String("AIRONLY WALL"), "DEFAULT_WL_AIR", String("Allows air, but blocks all particles.")}, + {0x575757_rgb, 0x000000_rgb, 1, Renderer::WallIcon, String("POWDER WALL"), "DEFAULT_WL_POWDR", String("Allows powders, blocks all other particles.")}, + {0xFFFF22_rgb, 0x101010_rgb, 2, Renderer::WallIcon, String("CONDUCTOR"), "DEFAULT_WL_CNDTR", String("Conductor. Allows all particles to pass through and conducts electricity.")}, + {0x242424_rgb, 0x101010_rgb, 0, Renderer::WallIcon, String("EHOLE"), "DEFAULT_WL_EHOLE", String("E-Hole. absorbs particles, releases them when powered.")}, + {0x579777_rgb, 0x000000_rgb, 1, Renderer::WallIcon, String("GAS WALL"), "DEFAULT_WL_GAS", String("Allows gases, blocks all other particles.")}, + {0xFFEE00_rgb, 0xAA9900_rgb, 4, Renderer::WallIcon, String("GRAVITY WALL"), "DEFAULT_WL_GRVTY", String("Gravity wall. Newtonian Gravity has no effect inside a box drawn with this.")}, + {0xFFAA00_rgb, 0xAA5500_rgb, 4, Renderer::WallIcon, String("ENERGY WALL"), "DEFAULT_WL_ENRGY", String("Allows energy particles, blocks all other particles.")}, + {0xDCDCDC_rgb, 0x000000_rgb, 1, Renderer::WallIcon, String("AIRBLOCK WALL"), "DEFAULT_WL_NOAIR", String("Allows all particles, but blocks air.")}, + {0x808080_rgb, 0x000000_rgb, 0, Renderer::WallIcon, String("ERASEALL"), "DEFAULT_WL_ERASEA", String("Erases walls, particles, and signs.")}, + {0x800080_rgb, 0x000000_rgb, 0, Renderer::WallIcon, String("STASIS WALL"), "DEFAULT_WL_STASIS", String("Freezes particles inside the wall in place until powered.")}, }; } diff --git a/src/simulation/SimulationData.h b/src/simulation/SimulationData.h index ce7a57872..08138e38f 100644 --- a/src/simulation/SimulationData.h +++ b/src/simulation/SimulationData.h @@ -1,138 +1,137 @@ -#ifndef SIMULATIONDATA_H -#define SIMULATIONDATA_H -#include "Config.h" - +#pragma once +#include "SimulationConfig.h" +#include #include #include -#define SC_WALL 0 -#define SC_ELEC 1 -#define SC_POWERED 2 -#define SC_SENSOR 3 -#define SC_FORCE 4 -#define SC_EXPLOSIVE 5 -#define SC_GAS 6 -#define SC_LIQUID 7 -#define SC_POWDERS 8 -#define SC_SOLIDS 9 -#define SC_NUCLEAR 10 -#define SC_SPECIAL 11 -#define SC_LIFE 12 -#define SC_TOOL 13 -#define SC_FAVORITES 14 -#define SC_DECO 15 -#define SC_CRACKER 16 -#define SC_CRACKER2 17 -#define SC_TOTAL 16 +constexpr int SC_WALL = 0; +constexpr int SC_ELEC = 1; +constexpr int SC_POWERED = 2; +constexpr int SC_SENSOR = 3; +constexpr int SC_FORCE = 4; +constexpr int SC_EXPLOSIVE = 5; +constexpr int SC_GAS = 6; +constexpr int SC_LIQUID = 7; +constexpr int SC_POWDERS = 8; +constexpr int SC_SOLIDS = 9; +constexpr int SC_NUCLEAR = 10; +constexpr int SC_SPECIAL = 11; +constexpr int SC_LIFE = 12; +constexpr int SC_TOOL = 13; +constexpr int SC_FAVORITES = 14; +constexpr int SC_DECO = 15; +constexpr int SC_CRACKER = 16; +constexpr int SC_CRACKER2 = 17; +constexpr int SC_TOTAL = 16; -#define O_WL_WALLELEC 122 -#define O_WL_EWALL 123 -#define O_WL_DETECT 124 -#define O_WL_STREAM 125 -#define O_WL_SIGN 126 -#define O_WL_FAN 127 -#define O_WL_FANHELPER 255 -#define O_WL_ALLOWLIQUID 128 -#define O_WL_DESTROYALL 129 -#define O_WL_ERASE 130 -#define O_WL_WALL 131 -#define O_WL_ALLOWAIR 132 -#define O_WL_ALLOWSOLID 133 -#define O_WL_ALLOWALLELEC 134 -#define O_WL_EHOLE 135 -#define O_WL_ALLOWGAS 140 -#define O_WL_GRAV 142 -#define O_WL_ALLOWENERGY 145 +constexpr int O_WL_WALLELEC = 122; +constexpr int O_WL_EWALL = 123; +constexpr int O_WL_DETECT = 124; +constexpr int O_WL_STREAM = 125; +constexpr int O_WL_SIGN = 126; +constexpr int O_WL_FAN = 127; +constexpr int O_WL_FANHELPER = 255; +constexpr int O_WL_ALLOWLIQUID = 128; +constexpr int O_WL_DESTROYALL = 129; +constexpr int O_WL_ERASE = 130; +constexpr int O_WL_WALL = 131; +constexpr int O_WL_ALLOWAIR = 132; +constexpr int O_WL_ALLOWSOLID = 133; +constexpr int O_WL_ALLOWALLELEC = 134; +constexpr int O_WL_EHOLE = 135; +constexpr int O_WL_ALLOWGAS = 140; +constexpr int O_WL_GRAV = 142; +constexpr int O_WL_ALLOWENERGY = 145; -#define WL_ERASE 0 -#define WL_WALLELEC 1 -#define WL_EWALL 2 -#define WL_DETECT 3 -#define WL_STREAM 4 -#define WL_FAN 5 -#define WL_ALLOWLIQUID 6 -#define WL_DESTROYALL 7 -#define WL_WALL 8 -#define WL_ALLOWAIR 9 -#define WL_ALLOWPOWDER 10 -#define WL_ALLOWALLELEC 11 -#define WL_EHOLE 12 -#define WL_ALLOWGAS 13 -#define WL_GRAV 14 -#define WL_ALLOWENERGY 15 -#define WL_BLOCKAIR 16 -#define WL_ERASEALL 17 -#define WL_STASIS 18 -#define WL_FLOODHELPER 255 +constexpr int WL_ERASE = 0; +constexpr int WL_WALLELEC = 1; +constexpr int WL_EWALL = 2; +constexpr int WL_DETECT = 3; +constexpr int WL_STREAM = 4; +constexpr int WL_FAN = 5; +constexpr int WL_ALLOWLIQUID = 6; +constexpr int WL_DESTROYALL = 7; +constexpr int WL_WALL = 8; +constexpr int WL_ALLOWAIR = 9; +constexpr int WL_ALLOWPOWDER = 10; +constexpr int WL_ALLOWALLELEC = 11; +constexpr int WL_EHOLE = 12; +constexpr int WL_ALLOWGAS = 13; +constexpr int WL_GRAV = 14; +constexpr int WL_ALLOWENERGY = 15; +constexpr int WL_BLOCKAIR = 16; +constexpr int WL_ERASEALL = 17; +constexpr int WL_STASIS = 18; +constexpr int WL_FLOODHELPER =255; -#define UI_WALLCOUNT 19 +constexpr int UI_WALLCOUNT = 19; -#define OLD_SPC_AIR 236 -#define SPC_AIR 256 +constexpr int OLD_SPC_AIR = 236; +constexpr int SPC_AIR = 256; -#define DECO_DRAW 0 -#define DECO_CLEAR 1 -#define DECO_ADD 2 -#define DECO_SUBTRACT 3 -#define DECO_MULTIPLY 4 -#define DECO_DIVIDE 5 -#define DECO_SMUDGE 6 +constexpr int DECO_DRAW = 0; +constexpr int DECO_CLEAR = 1; +constexpr int DECO_ADD = 2; +constexpr int DECO_SUBTRACT = 3; +constexpr int DECO_MULTIPLY = 4; +constexpr int DECO_DIVIDE = 5; +constexpr int DECO_SMUDGE = 6; //Old IDs for GOL types -#define GT_GOL 78 -#define GT_HLIF 79 -#define GT_ASIM 80 -#define GT_2x2 81 -#define GT_DANI 82 -#define GT_AMOE 83 -#define GT_MOVE 84 -#define GT_PGOL 85 -#define GT_DMOE 86 -#define GT_34 87 -#define GT_LLIF 88 -#define GT_STAN 89 -#define GT_SEED 134 -#define GT_MAZE 135 -#define GT_COAG 136 -#define GT_WALL 137 -#define GT_GNAR 138 -#define GT_REPL 139 -#define GT_MYST 140 -#define GT_LOTE 142 -#define GT_FRG2 143 -#define GT_STAR 144 -#define GT_FROG 145 -#define GT_BRAN 146 +constexpr int GT_GOL = 78; +constexpr int GT_HLIF = 79; +constexpr int GT_ASIM = 80; +constexpr int GT_2x2 = 81; +constexpr int GT_DANI = 82; +constexpr int GT_AMOE = 83; +constexpr int GT_MOVE = 84; +constexpr int GT_PGOL = 85; +constexpr int GT_DMOE = 86; +constexpr int GT_34 = 87; +constexpr int GT_LLIF = 88; +constexpr int GT_STAN = 89; +constexpr int GT_SEED = 134; +constexpr int GT_MAZE = 135; +constexpr int GT_COAG = 136; +constexpr int GT_WALL = 137; +constexpr int GT_GNAR = 138; +constexpr int GT_REPL = 139; +constexpr int GT_MYST = 140; +constexpr int GT_LOTE = 142; +constexpr int GT_FRG2 = 143; +constexpr int GT_STAR = 144; +constexpr int GT_FROG = 145; +constexpr int GT_BRAN = 146; //New IDs for GOL types -#define NGT_GOL 0 -#define NGT_HLIF 1 -#define NGT_ASIM 2 -#define NGT_2x2 3 -#define NGT_DANI 4 -#define NGT_AMOE 5 -#define NGT_MOVE 6 -#define NGT_PGOL 7 -#define NGT_DMOE 8 -#define NGT_34 9 -#define NGT_LLIF 10 -#define NGT_STAN 11 -#define NGT_SEED 12 -#define NGT_MAZE 13 -#define NGT_COAG 14 -#define NGT_WALL 15 -#define NGT_GNAR 16 -#define NGT_REPL 17 -#define NGT_MYST 18 -#define NGT_LOTE 19 -#define NGT_FRG2 20 -#define NGT_STAR 21 -#define NGT_FROG 22 -#define NGT_BRAN 23 +constexpr int NGT_GOL = 0; +constexpr int NGT_HLIF = 1; +constexpr int NGT_ASIM = 2; +constexpr int NGT_2x2 = 3; +constexpr int NGT_DANI = 4; +constexpr int NGT_AMOE = 5; +constexpr int NGT_MOVE = 6; +constexpr int NGT_PGOL = 7; +constexpr int NGT_DMOE = 8; +constexpr int NGT_34 = 9; +constexpr int NGT_LLIF = 10; +constexpr int NGT_STAN = 11; +constexpr int NGT_SEED = 12; +constexpr int NGT_MAZE = 13; +constexpr int NGT_COAG = 14; +constexpr int NGT_WALL = 15; +constexpr int NGT_GNAR = 16; +constexpr int NGT_REPL = 17; +constexpr int NGT_MYST = 18; +constexpr int NGT_LOTE = 19; +constexpr int NGT_FRG2 = 20; +constexpr int NGT_STAR = 21; +constexpr int NGT_FROG = 22; +constexpr int NGT_BRAN = 23; //replace mode / specific delete flags -#define REPLACE_MODE 0x1 -#define SPECIFIC_DELETE 0x2 +constexpr auto REPLACE_MODE = UINT32_C(0x00000001); +constexpr auto SPECIFIC_DELETE = UINT32_C(0x00000002); struct part_type; struct part_transition; @@ -151,5 +150,3 @@ std::vector LoadWalls(); std::vector LoadMenus(); std::vector LoadLatent(); - -#endif /* SIMULATIONDATA_H */ diff --git a/src/simulation/Snapshot.cpp b/src/simulation/Snapshot.cpp new file mode 100644 index 000000000..d4d70c72a --- /dev/null +++ b/src/simulation/Snapshot.cpp @@ -0,0 +1,43 @@ +#include "Snapshot.h" + +uint32_t Snapshot::Hash() const +{ + // http://www.isthe.com/chongo/tech/comp/fnv/ + auto hash = UINT32_C(2166136261); + auto take = [&hash](const uint8_t *data, size_t size) { + for (auto i = 0U; i < size; ++i) + { + hash ^= data[i]; + hash *= UINT32_C(16777619); + } + }; + auto takeThing = [&take](auto &thing) { + take(reinterpret_cast(&thing), sizeof(thing)); + }; + auto takeVector = [&take](auto &vec) { + take(reinterpret_cast(vec.data()), vec.size() * sizeof(vec[0])); + }; + takeVector(AirPressure); + takeVector(AirVelocityX); + takeVector(AirVelocityY); + takeVector(AmbientHeat); + takeVector(Particles); + takeVector(GravVelocityX); + takeVector(GravVelocityY); + takeVector(GravValue); + takeVector(GravMap); + takeVector(BlockMap); + takeVector(ElecMap); + takeVector(BlockAir); + takeVector(BlockAirH); + takeVector(FanVelocityX); + takeVector(FanVelocityY); + takeVector(PortalParticles); + takeVector(WirelessData); + takeVector(stickmen); + takeThing(FrameCount); + takeThing(RngState[0]); + takeThing(RngState[1]); + // signs and Authors are excluded on purpose, as they aren't POD and don't have much effect on the simulation. + return hash; +} diff --git a/src/simulation/Snapshot.h b/src/simulation/Snapshot.h index dfa1e5629..e9fb52be9 100644 --- a/src/simulation/Snapshot.h +++ b/src/simulation/Snapshot.h @@ -1,10 +1,10 @@ #pragma once - -#include - #include "Particle.h" #include "Sign.h" #include "Stickman.h" +#include "common/tpt-rand.h" +#include +#include #include class Snapshot @@ -24,6 +24,8 @@ public: std::vector BlockMap; std::vector ElecMap; + std::vector BlockAir; + std::vector BlockAirH; std::vector FanVelocityX; std::vector FanVelocityY; @@ -34,32 +36,12 @@ public: std::vector stickmen; std::vector signs; + uint64_t FrameCount; + RNG::State RngState; + + uint32_t Hash() const; + Json::Value Authors; - Snapshot() : - AirPressure(), - AirVelocityX(), - AirVelocityY(), - AmbientHeat(), - Particles(), - GravVelocityX(), - GravVelocityY(), - GravValue(), - GravMap(), - BlockMap(), - ElecMap(), - FanVelocityX(), - FanVelocityY(), - PortalParticles(), - WirelessData(), - stickmen(), - signs() - { - - } - - virtual ~Snapshot() - { - - } + virtual ~Snapshot() = default; }; diff --git a/src/simulation/SnapshotDelta.cpp b/src/simulation/SnapshotDelta.cpp index 8d0b8a3c7..b1e33c562 100644 --- a/src/simulation/SnapshotDelta.cpp +++ b/src/simulation/SnapshotDelta.cpp @@ -1,7 +1,5 @@ #include "SnapshotDelta.h" - #include "common/tpt-minmax.h" - #include // * A SnapshotDelta is a bidirectional difference type between Snapshots, defined such @@ -16,7 +14,7 @@ // size" even if their sizes weren't derived from compile-time constants, as they'd still // be the same size throughout the life of a Simulation, and thus any Snapshot created from it. // * Fields of dynamic size, whose sizes may be different between Snapshots. These are, fortunately, -// the minority: Particles, signs and Authors. +// the minority: Particles, signs, etc. // * Each field in Snapshot has a mirror set of fields in SnapshotDelta. Fields of static size // have mirror fields whose type is HunkVector, templated by the item type of the // corresponding field; these fields are handled in a uniform manner. Fields of dynamic size are @@ -36,7 +34,7 @@ // * ApplyHunkVector is the B = A + d operation, which takes a field of a SnapshotDelta and // the corresponding field of an "older" Snapshot, and fills the latter with the "new" values. // * This difference type is intended for fields of static size. This covers all fields in Snapshot -// except for Particles, signs, and Authors. +// except for Particles, signs, Authors, FrameCount, and RngState. // * A SingleDiff is, unsurprisingly enough, a single Diff, with an accompanying bool that signifies // whether the Diff does in fact hold the "old" value of a field in the "old" Snapshot and the "new" // value of the same field in the "new" Snapshot. If this bool is false, the data in the fields @@ -45,7 +43,8 @@ // * FillSingleDiff is the d = B - A operation, while ApplySingleDiff and ApplySingleDiff // are the A = B - d and B = A + d operations. These are self-explanatory. // * This difference type is intended for fields of dynamic size whose data doesn't change often and -// doesn't consume too much memory. This covers the Snapshot fields signs and Authors. +// doesn't consume too much memory. This covers the Snapshot fields signs and Authors, FrameCount, +// and RngState. // * This leaves Snapshot::Particles. This field mirrors Simulation::parts, which is actually also // a field of static size, but since most of the time most of this array is empty, it doesn't make // sense to store all of it in a Snapshot (unlike Air::hv, which can be fairly chaotic (i.e. may have @@ -214,11 +213,15 @@ std::unique_ptr SnapshotDelta::FromSnapshots(const Snapshot &oldS FillHunkVector(oldSnap.GravMap , newSnap.GravMap , delta.GravMap ); FillHunkVector(oldSnap.BlockMap , newSnap.BlockMap , delta.BlockMap ); FillHunkVector(oldSnap.ElecMap , newSnap.ElecMap , delta.ElecMap ); + FillHunkVector(oldSnap.BlockAir , newSnap.BlockAir , delta.BlockAir ); + FillHunkVector(oldSnap.BlockAirH , newSnap.BlockAirH , delta.BlockAirH ); FillHunkVector(oldSnap.FanVelocityX , newSnap.FanVelocityX , delta.FanVelocityX ); FillHunkVector(oldSnap.FanVelocityY , newSnap.FanVelocityY , delta.FanVelocityY ); FillHunkVector(oldSnap.WirelessData , newSnap.WirelessData , delta.WirelessData ); FillSingleDiff(oldSnap.signs , newSnap.signs , delta.signs ); FillSingleDiff(oldSnap.Authors , newSnap.Authors , delta.Authors ); + FillSingleDiff(oldSnap.FrameCount , newSnap.FrameCount , delta.FrameCount ); + FillSingleDiff(oldSnap.RngState , newSnap.RngState , delta.RngState ); FillHunkVectorPtr(reinterpret_cast(&oldSnap.PortalParticles[0]), reinterpret_cast(&newSnap.PortalParticles[0]), delta.PortalParticles, newSnap.PortalParticles.size() * ParticleUint32Count); FillHunkVectorPtr(reinterpret_cast(&oldSnap.stickmen[0]) , reinterpret_cast(&newSnap.stickmen[0] ), delta.stickmen , newSnap.stickmen .size() * playerstUint32Count); @@ -247,11 +250,15 @@ std::unique_ptr SnapshotDelta::Forward(const Snapshot &oldSnap) ApplyHunkVector(GravMap , newSnap.GravMap ); ApplyHunkVector(BlockMap , newSnap.BlockMap ); ApplyHunkVector(ElecMap , newSnap.ElecMap ); + ApplyHunkVector(BlockAir , newSnap.BlockAir ); + ApplyHunkVector(BlockAirH , newSnap.BlockAirH ); ApplyHunkVector(FanVelocityX , newSnap.FanVelocityX ); ApplyHunkVector(FanVelocityY , newSnap.FanVelocityY ); ApplyHunkVector(WirelessData , newSnap.WirelessData ); ApplySingleDiff(signs , newSnap.signs ); ApplySingleDiff(Authors , newSnap.Authors ); + ApplySingleDiff(FrameCount , newSnap.FrameCount ); + ApplySingleDiff(RngState , newSnap.RngState ); ApplyHunkVectorPtr(PortalParticles, reinterpret_cast(&newSnap.PortalParticles[0])); ApplyHunkVectorPtr(stickmen , reinterpret_cast(&newSnap.stickmen[0] )); @@ -278,11 +285,15 @@ std::unique_ptr SnapshotDelta::Restore(const Snapshot &newSnap) ApplyHunkVector(GravMap , oldSnap.GravMap ); ApplyHunkVector(BlockMap , oldSnap.BlockMap ); ApplyHunkVector(ElecMap , oldSnap.ElecMap ); + ApplyHunkVector(BlockAir , oldSnap.BlockAir ); + ApplyHunkVector(BlockAirH , oldSnap.BlockAirH ); ApplyHunkVector(FanVelocityX , oldSnap.FanVelocityX ); ApplyHunkVector(FanVelocityY , oldSnap.FanVelocityY ); ApplyHunkVector(WirelessData , oldSnap.WirelessData ); ApplySingleDiff(signs , oldSnap.signs ); ApplySingleDiff(Authors , oldSnap.Authors ); + ApplySingleDiff(FrameCount , oldSnap.FrameCount ); + ApplySingleDiff(RngState , oldSnap.RngState ); ApplyHunkVectorPtr(PortalParticles, reinterpret_cast(&oldSnap.PortalParticles[0])); ApplyHunkVectorPtr(stickmen , reinterpret_cast(&oldSnap.stickmen[0] )); diff --git a/src/simulation/SnapshotDelta.h b/src/simulation/SnapshotDelta.h index 1ceb9397b..dfc004846 100644 --- a/src/simulation/SnapshotDelta.h +++ b/src/simulation/SnapshotDelta.h @@ -1,9 +1,7 @@ #pragma once - -#include "Snapshot.h" - #include #include +#include "Snapshot.h" struct SnapshotDelta { @@ -58,6 +56,8 @@ struct SnapshotDelta HunkVector BlockMap; HunkVector ElecMap; + HunkVector BlockAir; + HunkVector BlockAirH; HunkVector FanVelocityX; HunkVector FanVelocityY; @@ -67,6 +67,8 @@ struct SnapshotDelta HunkVector WirelessData; HunkVector stickmen; SingleDiff> signs; + SingleDiff FrameCount; + SingleDiff RngState; SingleDiff Authors; diff --git a/src/simulation/Stickman.h b/src/simulation/Stickman.h index 77fb291db..d27e42ef9 100644 --- a/src/simulation/Stickman.h +++ b/src/simulation/Stickman.h @@ -1,7 +1,6 @@ -#ifndef STICKMAN_H_ -#define STICKMAN_H_ +#pragma once -#define MAX_FIGHTERS 100 +constexpr auto MAX_FIGHTERS = 100; struct playerst { char comm; //command cell @@ -15,5 +14,3 @@ struct playerst bool fan; int spawnID; //id of the SPWN particle that spawns it }; - -#endif diff --git a/src/simulation/StorageClasses.h b/src/simulation/StorageClasses.h deleted file mode 100644 index df94eadb9..000000000 --- a/src/simulation/StorageClasses.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef STORAGECLASSES_H_ -#define STORAGECLASSES_H_ - -#include "ElementDefs.h" -#include "ElementClasses.h" - -class Renderer; -class Simulation; - -/*struct part_type -{ - char *name; - pixel pcolors; - float advection; - float airdrag; - float airloss; - float loss; - float collision; - float gravity; - float diffusion; - float hotair; - int falldown; - int flammable; - int explosive; - int meltable; - int hardness; - int menu; - int enabled; - int weight; - int menusection; - float heat; - unsigned char hconduct; - char *descs; - char state; - unsigned int properties; - int (*update_func) (UPDATE_FUNC_ARGS); - int (*graphics_func) (GRAPHICS_FUNC_ARGS); -}; -typedef struct part_type part_type;*/ - -/*struct part_transition -{ - float plv; // transition occurs if pv is lower than this - int plt; - float phv; // transition occurs if pv is higher than this - int pht; - float tlv; // transition occurs if t is lower than this - int tlt; - float thv; // transition occurs if t is higher than this - int tht; -}; -typedef struct part_transition part_transition;*/ - -#endif diff --git a/src/simulation/StructProperty.h b/src/simulation/StructProperty.h index a2c5bc8ad..3a2aba849 100644 --- a/src/simulation/StructProperty.h +++ b/src/simulation/StructProperty.h @@ -1,6 +1,4 @@ -#ifndef STRUCTPROPERTY_H_ -#define STRUCTPROPERTY_H_ - +#pragma once #include "common/String.h" #include @@ -50,5 +48,3 @@ struct StructPropertyAlias { ByteString from, to; }; - -#endif diff --git a/src/simulation/ToolClasses.h b/src/simulation/ToolClasses.h index 07256e27c..eb947e616 100644 --- a/src/simulation/ToolClasses.h +++ b/src/simulation/ToolClasses.h @@ -1,6 +1,4 @@ -#ifndef TOOLCLASSES_H -#define TOOLCLASSES_H - +#pragma once #include #include @@ -11,5 +9,3 @@ #undef TOOL_NUMBERS_ENUMERATE std::vector const &GetTools(); - -#endif diff --git a/src/simulation/ToolCommon.h b/src/simulation/ToolCommon.h index 1b38e7d9b..84f4e1976 100644 --- a/src/simulation/ToolCommon.h +++ b/src/simulation/ToolCommon.h @@ -1,12 +1,6 @@ -#ifndef SIMTOOLCOMMON_H -#define SIMTOOLCOMMON_H - -#include "Config.h" - +#pragma once #include "ToolClasses.h" #include "Simulation.h" #include "graphics/Renderer.h" #include "ElementDefs.h" #include "ElementClasses.h" - -#endif // SIMTOOLCOMMON_H diff --git a/src/simulation/WallType.h b/src/simulation/WallType.h index 79478d5e0..62f19b63a 100644 --- a/src/simulation/WallType.h +++ b/src/simulation/WallType.h @@ -1,19 +1,17 @@ -#ifndef WALLTYPE_H_ -#define WALLTYPE_H_ - +#pragma once +#include #include "common/String.h" +#include "common/Vec2.h" #include "graphics/Pixel.h" class VideoBuffer; struct wall_type { - pixel colour; - pixel eglow; // if emap set, add this to fire glow + RGB colour; + RGB eglow; // if emap set, add this to fire glow int drawstyle; - VideoBuffer * (*textureGen)(int, int, int); + std::unique_ptr (*textureGen)(int, Vec2); String name; ByteString identifier; String descs; }; - -#endif diff --git a/src/simulation/elements/ACEL.cpp b/src/simulation/elements/ACEL.cpp index 7de07fb20..042408633 100644 --- a/src/simulation/elements/ACEL.cpp +++ b/src/simulation/elements/ACEL.cpp @@ -7,7 +7,7 @@ void Element::Element_ACEL() { Identifier = "DEFAULT_PT_ACEL"; Name = "ACEL"; - Colour = PIXPACK(0x0099CC); + Colour = 0x0099CC_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; diff --git a/src/simulation/elements/ACID.cpp b/src/simulation/elements/ACID.cpp index cd5656994..6ff8490f6 100644 --- a/src/simulation/elements/ACID.cpp +++ b/src/simulation/elements/ACID.cpp @@ -7,7 +7,7 @@ void Element::Element_ACID() { Identifier = "DEFAULT_PT_ACID"; Name = "ACID"; - Colour = PIXPACK(0xED55FF); + Colour = 0xED55FF_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -71,14 +71,14 @@ static int update(UPDATE_FUNC_ARGS) } else if (rt == PT_WTRV) { - if (RNG::Ref().chance(1, 250)) + if (sim->rng.chance(1, 250)) { sim->part_change_type(i, x, y, PT_CAUS); - parts[i].life = RNG::Ref().between(25, 74); + parts[i].life = sim->rng.between(25, 74); sim->kill_part(ID(r)); } } - else if (rt != PT_CLNE && rt != PT_PCLN && parts[i].life >= 50 && RNG::Ref().chance(sim->elements[rt].Hardness, 1000)) + else if (rt != PT_CLNE && rt != PT_PCLN && parts[i].life >= 50 && sim->rng.chance(sim->elements[rt].Hardness, 1000)) { if (sim->parts_avg(i, ID(r),PT_GLAS)!= PT_GLAS)//GLAS protects stuff from acid { @@ -109,8 +109,8 @@ static int update(UPDATE_FUNC_ARGS) } for (trade = 0; trade<2; trade++) { - rx = RNG::Ref().between(-2, 2); - ry = RNG::Ref().between(-2, 2); + rx = sim->rng.between(-2, 2); + ry = sim->rng.between(-2, 2); if (BOUNDS_CHECK && (rx || ry)) { r = pmap[y+ry][x+rx]; diff --git a/src/simulation/elements/AMTR.cpp b/src/simulation/elements/AMTR.cpp index bcf4428d4..bb4648358 100644 --- a/src/simulation/elements/AMTR.cpp +++ b/src/simulation/elements/AMTR.cpp @@ -7,7 +7,7 @@ void Element::Element_AMTR() { Identifier = "DEFAULT_PT_AMTR"; Name = "AMTR"; - Colour = PIXPACK(0x808080); + Colour = 0x808080_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -66,7 +66,7 @@ static int update(UPDATE_FUNC_ARGS) sim->kill_part(i); return 1; } - if (RNG::Ref().chance(1, 10)) + if (sim->rng.chance(1, 10)) sim->create_part(ID(r), x+rx, y+ry, PT_PHOT); else sim->kill_part(ID(r)); diff --git a/src/simulation/elements/ANAR.cpp b/src/simulation/elements/ANAR.cpp index 6864a75cf..969038926 100644 --- a/src/simulation/elements/ANAR.cpp +++ b/src/simulation/elements/ANAR.cpp @@ -6,7 +6,7 @@ void Element::Element_ANAR() { Identifier = "DEFAULT_PT_ANAR"; Name = "ANAR"; - Colour = PIXPACK(0xFFFFEE); + Colour = 0xFFFFEE_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -59,10 +59,10 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)==PT_CFLM && RNG::Ref().chance(1, 4)) + if (TYP(r)==PT_CFLM && sim->rng.chance(1, 4)) { sim->part_change_type(i,x,y,PT_CFLM); - parts[i].life = RNG::Ref().between(50, 199); + parts[i].life = sim->rng.between(50, 199); parts[ID(r)].temp = parts[i].temp = 0; sim->pv[y/CELL][x/CELL] -= 0.5; } diff --git a/src/simulation/elements/ARAY.cpp b/src/simulation/elements/ARAY.cpp index 745ff1a2c..a7558d3df 100644 --- a/src/simulation/elements/ARAY.cpp +++ b/src/simulation/elements/ARAY.cpp @@ -6,7 +6,7 @@ void Element::Element_ARAY() { Identifier = "DEFAULT_PT_ARAY"; Name = "ARAY"; - Colour = PIXPACK(0xFFBB00); + Colour = 0xFFBB00_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; @@ -128,8 +128,8 @@ static int update(UPDATE_FUNC_ARGS) { if (parts[r].tmp != 6) { - int Element_FILT_interactWavelengths(Particle* cpart, int origWl); - colored = Element_FILT_interactWavelengths(&parts[r], colored); + int Element_FILT_interactWavelengths(Simulation *sim, Particle* cpart, int origWl); + colored = Element_FILT_interactWavelengths(sim, &parts[r], colored); if (!colored) break; } diff --git a/src/simulation/elements/BANG.cpp b/src/simulation/elements/BANG.cpp index ceb5fe1b9..0f3c82592 100644 --- a/src/simulation/elements/BANG.cpp +++ b/src/simulation/elements/BANG.cpp @@ -6,7 +6,7 @@ void Element::Element_BANG() { Identifier = "DEFAULT_PT_BANG"; Name = "TNT"; - Colour = PIXPACK(0xC05050); + Colour = 0xC05050_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -87,29 +87,29 @@ static int update(UPDATE_FUNC_ARGS) //Explode!! sim->pv[y/CELL][x/CELL] += 0.5f; parts[i].tmp = 0; - if (RNG::Ref().chance(1, 3)) + if (sim->rng.chance(1, 3)) { - if (RNG::Ref().chance(1, 2)) + if (sim->rng.chance(1, 2)) { sim->create_part(i, x, y, PT_FIRE); } else { sim->create_part(i, x, y, PT_SMKE); - parts[i].life = RNG::Ref().between(500, 549); + parts[i].life = sim->rng.between(500, 549); } parts[i].temp = restrict_flt((MAX_TEMP/4)+otemp, MIN_TEMP, MAX_TEMP); } else { - if (RNG::Ref().chance(1, 15)) + if (sim->rng.chance(1, 15)) { sim->create_part(i, x, y, PT_EMBR); parts[i].tmp = 0; parts[i].life = 50; parts[i].temp = restrict_flt((MAX_TEMP/3)+otemp, MIN_TEMP, MAX_TEMP); - parts[i].vx = float(RNG::Ref().between(-10, 10)); - parts[i].vy = float(RNG::Ref().between(-10, 10)); + parts[i].vx = float(sim->rng.between(-10, 10)); + parts[i].vy = float(sim->rng.between(-10, 10)); } else { diff --git a/src/simulation/elements/BCLN.cpp b/src/simulation/elements/BCLN.cpp index 6f1158775..2a1135b49 100644 --- a/src/simulation/elements/BCLN.cpp +++ b/src/simulation/elements/BCLN.cpp @@ -6,7 +6,7 @@ void Element::Element_BCLN() { Identifier = "DEFAULT_PT_BCLN"; Name = "BCLN"; - Colour = PIXPACK(0xFFD040); + Colour = 0xFFD040_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -32,6 +32,7 @@ void Element::Element_BCLN() Description = "Breakable Clone."; Properties = TYPE_SOLID | PROP_LIFE_DEC | PROP_LIFE_KILL_DEC | PROP_NOCTYPEDRAW; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -51,7 +52,7 @@ constexpr float ADVECTION = 0.1f; static int update(UPDATE_FUNC_ARGS) { if (!parts[i].life && sim->pv[y/CELL][x/CELL]>4.0f) - parts[i].life = RNG::Ref().between(80, 119); + parts[i].life = sim->rng.between(80, 119); if (parts[i].life) { parts[i].vx += ADVECTION*sim->vx[y/CELL][x/CELL]; @@ -83,10 +84,10 @@ static int update(UPDATE_FUNC_ARGS) } else { - if (parts[i].ctype==PT_LIFE) sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), PT_LIFE, parts[i].tmp); - else if (parts[i].ctype!=PT_LIGH || RNG::Ref().chance(1, 30)) + if (parts[i].ctype==PT_LIFE) sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), PT_LIFE, parts[i].tmp); + else if (parts[i].ctype!=PT_LIGH || sim->rng.chance(1, 30)) { - int np = sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), TYP(parts[i].ctype)); + int np = sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), TYP(parts[i].ctype)); if (np>=0) { if (parts[i].ctype==PT_LAVA && parts[i].tmp>0 && parts[i].tmpelements[parts[i].tmp].HighTemperatureTransition==PT_LAVA) diff --git a/src/simulation/elements/BCOL.cpp b/src/simulation/elements/BCOL.cpp index 642f85b07..7fc9f6932 100644 --- a/src/simulation/elements/BCOL.cpp +++ b/src/simulation/elements/BCOL.cpp @@ -7,7 +7,7 @@ void Element::Element_BCOL() { Identifier = "DEFAULT_PT_BCOL"; Name = "BCOL"; - Colour = PIXPACK(0x333333); + Colour = 0x333333_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/BGLA.cpp b/src/simulation/elements/BGLA.cpp index 729fd4804..9b02e3da6 100644 --- a/src/simulation/elements/BGLA.cpp +++ b/src/simulation/elements/BGLA.cpp @@ -4,7 +4,7 @@ void Element::Element_BGLA() { Identifier = "DEFAULT_PT_BGLA"; Name = "BGLA"; - Colour = PIXPACK(0x606060); + Colour = 0x606060_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/BHOL.cpp b/src/simulation/elements/BHOL.cpp index bad62d00b..250ea082c 100644 --- a/src/simulation/elements/BHOL.cpp +++ b/src/simulation/elements/BHOL.cpp @@ -4,7 +4,7 @@ void Element::Element_BHOL() { Identifier = "DEFAULT_PT_BHOL"; Name = "VACU"; - Colour = PIXPACK(0x303030); + Colour = 0x303030_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; diff --git a/src/simulation/elements/BIZR.cpp b/src/simulation/elements/BIZR.cpp index 64f907868..350698ab1 100644 --- a/src/simulation/elements/BIZR.cpp +++ b/src/simulation/elements/BIZR.cpp @@ -7,7 +7,7 @@ void Element::Element_BIZR() { Identifier = "DEFAULT_PT_BIZR"; Name = "BIZR"; - Colour = PIXPACK(0x00FF77); + Colour = 0x00FF77_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/BIZRG.cpp b/src/simulation/elements/BIZRG.cpp index 33e2fd509..012772f92 100644 --- a/src/simulation/elements/BIZRG.cpp +++ b/src/simulation/elements/BIZRG.cpp @@ -7,7 +7,7 @@ void Element::Element_BIZRG() { Identifier = "DEFAULT_PT_BIZRG"; Name = "BIZG"; - Colour = PIXPACK(0x00FFBB); + Colour = 0x00FFBB_rgb; MenuVisible = 1; MenuSection = SC_CRACKER2; Enabled = 1; diff --git a/src/simulation/elements/BIZRS.cpp b/src/simulation/elements/BIZRS.cpp index 77757e1e8..a8d890991 100644 --- a/src/simulation/elements/BIZRS.cpp +++ b/src/simulation/elements/BIZRS.cpp @@ -7,7 +7,7 @@ void Element::Element_BIZRS() { Identifier = "DEFAULT_PT_BIZRS"; Name = "BIZS"; - Colour = PIXPACK(0x00E455); + Colour = 0x00E455_rgb; MenuVisible = 1; MenuSection = SC_CRACKER2; Enabled = 1; diff --git a/src/simulation/elements/BMTL.cpp b/src/simulation/elements/BMTL.cpp index aafaf0a1c..2e00b8c25 100644 --- a/src/simulation/elements/BMTL.cpp +++ b/src/simulation/elements/BMTL.cpp @@ -6,7 +6,7 @@ void Element::Element_BMTL() { Identifier = "DEFAULT_PT_BMTL"; Name = "BMTL"; - Colour = PIXPACK(0x505070); + Colour = 0x505070_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -58,14 +58,14 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_METL || TYP(r)==PT_IRON) && RNG::Ref().chance(1, 100)) + if ((TYP(r)==PT_METL || TYP(r)==PT_IRON) && sim->rng.chance(1, 100)) { sim->part_change_type(ID(r),x+rx,y+ry,PT_BMTL); - parts[ID(r)].tmp = (parts[i].tmp<=7) ? parts[i].tmp=1 : parts[i].tmp - RNG::Ref().between(0, 4); + parts[ID(r)].tmp = (parts[i].tmp<=7) ? parts[i].tmp=1 : parts[i].tmp - sim->rng.between(0, 4); } } } - else if (parts[i].tmp==1 && RNG::Ref().chance(1, 1000)) + else if (parts[i].tmp==1 && sim->rng.chance(1, 1000)) { parts[i].tmp = 0; sim->part_change_type(i,x,y,PT_BRMT); diff --git a/src/simulation/elements/BOMB.cpp b/src/simulation/elements/BOMB.cpp index 6f3409813..d530067b7 100644 --- a/src/simulation/elements/BOMB.cpp +++ b/src/simulation/elements/BOMB.cpp @@ -7,7 +7,7 @@ void Element::Element_BOMB() { Identifier = "DEFAULT_PT_BOMB"; Name = "BOMB"; - Colour = PIXPACK(0xFFF288); + Colour = 0xFFF288_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -99,8 +99,8 @@ static int update(UPDATE_FUNC_ARGS) parts[nb].tmp = 0; parts[nb].life = 50; parts[nb].temp = MAX_TEMP; - parts[nb].vx = float(RNG::Ref().between(-20, 20)); - parts[nb].vy = float(RNG::Ref().between(-20, 20)); + parts[nb].vx = float(sim->rng.between(-20, 20)); + parts[nb].vy = float(sim->rng.between(-20, 20)); } } return 1; diff --git a/src/simulation/elements/BOYL.cpp b/src/simulation/elements/BOYL.cpp index 831970f94..8adabe4cf 100644 --- a/src/simulation/elements/BOYL.cpp +++ b/src/simulation/elements/BOYL.cpp @@ -6,7 +6,7 @@ void Element::Element_BOYL() { Identifier = "DEFAULT_PT_BOYL"; Name = "BOYL"; - Colour = PIXPACK(0x0A3200); + Colour = 0x0A3200_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -71,12 +71,12 @@ static int update(UPDATE_FUNC_ARGS) continue; if (TYP(r)==PT_WATR) { - if (RNG::Ref().chance(1, 30)) + if (sim->rng.chance(1, 30)) sim->part_change_type(ID(r),x+rx,y+ry,PT_FOG); } else if (TYP(r)==PT_O2) { - if (RNG::Ref().chance(1, 9)) + if (sim->rng.chance(1, 9)) { sim->kill_part(ID(r)); sim->part_change_type(i,x,y,PT_WATR); diff --git a/src/simulation/elements/BRAY.cpp b/src/simulation/elements/BRAY.cpp index 902164dfb..bbb5134af 100644 --- a/src/simulation/elements/BRAY.cpp +++ b/src/simulation/elements/BRAY.cpp @@ -6,7 +6,7 @@ void Element::Element_BRAY() { Identifier = "DEFAULT_PT_BRAY"; Name = "BRAY"; - Colour = PIXPACK(0xFFFFFF); + Colour = 0xFFFFFF_rgb; MenuVisible = 0; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/BRCK.cpp b/src/simulation/elements/BRCK.cpp index 18216e40d..e8a60fcec 100644 --- a/src/simulation/elements/BRCK.cpp +++ b/src/simulation/elements/BRCK.cpp @@ -6,7 +6,7 @@ void Element::Element_BRCK() { Identifier = "DEFAULT_PT_BRCK"; Name = "BRCK"; - Colour = PIXPACK(0x808080); + Colour = 0x808080_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/BREC.cpp b/src/simulation/elements/BREC.cpp index 070514599..c577a8ceb 100644 --- a/src/simulation/elements/BREC.cpp +++ b/src/simulation/elements/BREC.cpp @@ -6,7 +6,7 @@ void Element::Element_BREC() { Identifier = "DEFAULT_PT_BREC"; Name = "BREL"; - Colour = PIXPACK(0x707060); + Colour = 0x707060_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -51,7 +51,7 @@ static int update(UPDATE_FUNC_ARGS) { if (sim->pv[y/CELL][x/CELL]>10.0f) { - if (parts[i].temp>9000 && sim->pv[y/CELL][x/CELL]>30.0f && RNG::Ref().chance(1, 200)) + if (parts[i].temp>9000 && sim->pv[y/CELL][x/CELL]>30.0f && sim->rng.chance(1, 200)) { sim->part_change_type(i, x ,y ,PT_EXOT); parts[i].life = 1000; diff --git a/src/simulation/elements/BRMT.cpp b/src/simulation/elements/BRMT.cpp index 5413a92da..19143a6f7 100644 --- a/src/simulation/elements/BRMT.cpp +++ b/src/simulation/elements/BRMT.cpp @@ -6,7 +6,7 @@ void Element::Element_BRMT() { Identifier = "DEFAULT_PT_BRMT"; Name = "BRMT"; - Colour = PIXPACK(0x705060); + Colour = 0x705060_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -60,9 +60,9 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)==PT_BREC && RNG::Ref().chance(1, tempFactor)) + if (TYP(r)==PT_BREC && sim->rng.chance(1, tempFactor)) { - if (RNG::Ref().chance(1, 2)) + if (sim->rng.chance(1, 2)) { sim->create_part(ID(r), x+rx, y+ry, PT_THRM); } diff --git a/src/simulation/elements/BTRY.cpp b/src/simulation/elements/BTRY.cpp index f3511bac7..b007e1231 100644 --- a/src/simulation/elements/BTRY.cpp +++ b/src/simulation/elements/BTRY.cpp @@ -6,7 +6,7 @@ void Element::Element_BTRY() { Identifier = "DEFAULT_PT_BTRY"; Name = "BTRY"; - Colour = PIXPACK(0x858505); + Colour = 0x858505_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/BVBR.cpp b/src/simulation/elements/BVBR.cpp index b32ead1e0..3a1b26686 100644 --- a/src/simulation/elements/BVBR.cpp +++ b/src/simulation/elements/BVBR.cpp @@ -7,7 +7,7 @@ void Element::Element_BVBR() { Identifier = "DEFAULT_PT_BVBR"; Name = "BVBR"; - Colour = PIXPACK(0x005000); + Colour = 0x005000_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; diff --git a/src/simulation/elements/C5.cpp b/src/simulation/elements/C5.cpp index 908eac6e9..2f9e278ba 100644 --- a/src/simulation/elements/C5.cpp +++ b/src/simulation/elements/C5.cpp @@ -7,7 +7,7 @@ void Element::Element_C5() { Identifier = "DEFAULT_PT_C5"; Name = "C-5"; - Colour = PIXPACK(0x2050E0); + Colour = 0x2050E0_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -59,11 +59,11 @@ static int update(UPDATE_FUNC_ARGS) continue; if ((TYP(r)!=PT_C5 && parts[ID(r)].temp<100 && sim->elements[TYP(r)].HeatConduct && (TYP(r)!=PT_HSWC||parts[ID(r)].life==10)) || TYP(r)==PT_CFLM) { - if (RNG::Ref().chance(1, 6)) + if (sim->rng.chance(1, 6)) { sim->part_change_type(i,x,y,PT_CFLM); parts[ID(r)].temp = parts[i].temp = 0; - parts[i].life = RNG::Ref().between(50, 199); + parts[i].life = sim->rng.between(50, 199); sim->pv[y/CELL][x/CELL] += 1.5; } } diff --git a/src/simulation/elements/CAUS.cpp b/src/simulation/elements/CAUS.cpp index 9aaec5e57..efa94055e 100644 --- a/src/simulation/elements/CAUS.cpp +++ b/src/simulation/elements/CAUS.cpp @@ -6,7 +6,7 @@ void Element::Element_CAUS() { Identifier = "DEFAULT_PT_CAUS"; Name = "CAUS"; - Colour = PIXPACK(0x80FFA0); + Colour = 0x80FFA0_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -66,7 +66,7 @@ static int update(UPDATE_FUNC_ARGS) } else if (TYP(r) != PT_ACID && TYP(r) != PT_CAUS && TYP(r) != PT_RFRG && TYP(r) != PT_RFGL) { - if ((TYP(r) != PT_CLNE && TYP(r) != PT_PCLN && RNG::Ref().chance(sim->elements[TYP(r)].Hardness, 1000)) && parts[i].life >= 50) + if ((TYP(r) != PT_CLNE && TYP(r) != PT_PCLN && sim->rng.chance(sim->elements[TYP(r)].Hardness, 1000)) && parts[i].life >= 50) { // GLAS protects stuff from acid if (sim->parts_avg(i, ID(r),PT_GLAS) != PT_GLAS) diff --git a/src/simulation/elements/CBNW.cpp b/src/simulation/elements/CBNW.cpp index 37e21cc95..83e9bfa48 100644 --- a/src/simulation/elements/CBNW.cpp +++ b/src/simulation/elements/CBNW.cpp @@ -7,7 +7,7 @@ void Element::Element_CBNW() { Identifier = "DEFAULT_PT_CBNW"; Name = "BUBW"; - Colour = PIXPACK(0x2030D0); + Colour = 0x2030D0_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -53,7 +53,7 @@ static int update(UPDATE_FUNC_ARGS) int r, rx, ry; if (sim->pv[y/CELL][x/CELL]<=3) { - if (sim->pv[y/CELL][x/CELL] <= -0.5 || RNG::Ref().chance(1, 4000)) + if (sim->pv[y/CELL][x/CELL] <= -0.5 || sim->rng.chance(1, 4000)) { sim->part_change_type(i,x,y,PT_CO2); parts[i].ctype = 5; @@ -63,15 +63,15 @@ static int update(UPDATE_FUNC_ARGS) if (parts[i].tmp2!=20) { parts[i].tmp2 -= (parts[i].tmp2>20)?1:-1; } - else if (RNG::Ref().chance(1, 200)) + else if (sim->rng.chance(1, 200)) { - parts[i].tmp2 = RNG::Ref().between(0, 39); + parts[i].tmp2 = sim->rng.between(0, 39); } if(parts[i].tmp>0) { //Explode - if(parts[i].tmp==1 && RNG::Ref().chance(3, 4)) + if(parts[i].tmp==1 && sim->rng.chance(3, 4)) { sim->part_change_type(i,x,y,PT_CO2); parts[i].ctype = 5; @@ -86,12 +86,12 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if ((sim->elements[TYP(r)].Properties&TYPE_PART) && parts[i].tmp == 0 && RNG::Ref().chance(1, 83)) + if ((sim->elements[TYP(r)].Properties&TYPE_PART) && parts[i].tmp == 0 && sim->rng.chance(1, 83)) { //Start explode - parts[i].tmp = RNG::Ref().between(0, 24); + parts[i].tmp = sim->rng.between(0, 24); } - else if((sim->elements[TYP(r)].Properties&TYPE_SOLID) && TYP(r)!=PT_DMND && TYP(r)!=PT_GLAS && parts[i].tmp == 0 && RNG::Ref().chance(int(2 - sim->pv[y/CELL][x/CELL]), 6667)) + else if((sim->elements[TYP(r)].Properties&TYPE_SOLID) && TYP(r)!=PT_DMND && TYP(r)!=PT_GLAS && parts[i].tmp == 0 && sim->rng.chance(int(2 - sim->pv[y/CELL][x/CELL]), 6667)) { sim->part_change_type(i,x,y,PT_CO2); parts[i].ctype = 5; @@ -117,7 +117,7 @@ static int update(UPDATE_FUNC_ARGS) } else if (TYP(r)==PT_RBDM||TYP(r)==PT_LRBD) { - if ((sim->legacy_enable||parts[i].temp>(273.15f+12.0f)) && RNG::Ref().chance(1, 166)) + if ((sim->legacy_enable||parts[i].temp>(273.15f+12.0f)) && sim->rng.chance(1, 166)) { sim->part_change_type(i,x,y,PT_FIRE); parts[i].life = 4; @@ -126,7 +126,7 @@ static int update(UPDATE_FUNC_ARGS) } else if (TYP(r)==PT_FIRE && parts[ID(r)].ctype!=PT_WATR){ sim->kill_part(ID(r)); - if (RNG::Ref().chance(1, 50)) + if (sim->rng.chance(1, 50)) { sim->kill_part(i); return 1; diff --git a/src/simulation/elements/CFLM.cpp b/src/simulation/elements/CFLM.cpp index cbc465af4..d34f9e043 100644 --- a/src/simulation/elements/CFLM.cpp +++ b/src/simulation/elements/CFLM.cpp @@ -7,7 +7,7 @@ void Element::Element_CFLM() { Identifier = "DEFAULT_PT_HFLM"; Name = "CFLM"; - Colour = PIXPACK(0x8080FF); + Colour = 0x8080FF_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -50,10 +50,10 @@ void Element::Element_CFLM() static int graphics(GRAPHICS_FUNC_ARGS) { - auto color = Renderer::clfmTableAt(cpart->life / 2); - *colr = PIXR(color); - *colg = PIXG(color); - *colb = PIXB(color); + RGB color = Renderer::clfmTableAt(cpart->life / 2); + *colr = color.Red; + *colg = color.Green; + *colb = color.Blue; *firea = 255; *firer = *colr; @@ -68,5 +68,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].life = RNG::Ref().between(50, 199); + sim->parts[i].life = sim->rng.between(50, 199); } diff --git a/src/simulation/elements/CLNE.cpp b/src/simulation/elements/CLNE.cpp index e7a8bd6d7..06c4e9844 100644 --- a/src/simulation/elements/CLNE.cpp +++ b/src/simulation/elements/CLNE.cpp @@ -6,7 +6,7 @@ void Element::Element_CLNE() { Identifier = "DEFAULT_PT_CLNE"; Name = "CLNE"; - Colour = PIXPACK(0xFFD010); + Colour = 0xFFD010_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -32,6 +32,7 @@ void Element::Element_CLNE() Description = "Clone. Duplicates any particles it touches."; Properties = TYPE_SOLID | PROP_NOCTYPEDRAW; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -74,10 +75,10 @@ static int update(UPDATE_FUNC_ARGS) } else { - if (parts[i].ctype==PT_LIFE) sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), PT_LIFE, parts[i].tmp); - else if (parts[i].ctype!=PT_LIGH || RNG::Ref().chance(1, 30)) + if (parts[i].ctype==PT_LIFE) sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), PT_LIFE, parts[i].tmp); + else if (parts[i].ctype!=PT_LIGH || sim->rng.chance(1, 30)) { - int np = sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), TYP(parts[i].ctype)); + int np = sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), TYP(parts[i].ctype)); if (np>=0) { if (parts[i].ctype==PT_LAVA && parts[i].tmp>0 && parts[i].tmpelements[parts[i].tmp].HighTemperatureTransition==PT_LAVA) diff --git a/src/simulation/elements/CLST.cpp b/src/simulation/elements/CLST.cpp index 25442207d..623a54a2e 100644 --- a/src/simulation/elements/CLST.cpp +++ b/src/simulation/elements/CLST.cpp @@ -8,7 +8,7 @@ void Element::Element_CLST() { Identifier = "DEFAULT_PT_CLST"; Name = "CLST"; - Colour = PIXPACK(0xE4A4A4); + Colour = 0xE4A4A4_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -62,7 +62,7 @@ static int update(UPDATE_FUNC_ARGS) continue; if (TYP(r)==PT_WATR) { - if (RNG::Ref().chance(1, 1500)) + if (sim->rng.chance(1, 1500)) { sim->create_part(i, x, y, PT_PSTS); sim->kill_part(ID(r)); @@ -101,5 +101,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].tmp = RNG::Ref().between(0, 6); + sim->parts[i].tmp = sim->rng.between(0, 6); } diff --git a/src/simulation/elements/CNCT.cpp b/src/simulation/elements/CNCT.cpp index 6f8645754..0d56d4d8b 100644 --- a/src/simulation/elements/CNCT.cpp +++ b/src/simulation/elements/CNCT.cpp @@ -4,7 +4,7 @@ void Element::Element_CNCT() { Identifier = "DEFAULT_PT_CNCT"; Name = "CNCT"; - Colour = PIXPACK(0xC0C0C0); + Colour = 0xC0C0C0_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/CO2.cpp b/src/simulation/elements/CO2.cpp index ca99fe5fb..f1605e015 100644 --- a/src/simulation/elements/CO2.cpp +++ b/src/simulation/elements/CO2.cpp @@ -6,7 +6,7 @@ void Element::Element_CO2() { Identifier = "DEFAULT_PT_CO2"; Name = "CO2"; - Colour = PIXPACK(0x666666); + Colour = 0x666666_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -55,7 +55,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) { - if (parts[i].ctype==5 && RNG::Ref().chance(1, 2000)) + if (parts[i].ctype==5 && sim->rng.chance(1, 2000)) { if (sim->create_part(-1, x+rx, y+ry, PT_WATR)>=0) parts[i].ctype = 0; @@ -65,13 +65,13 @@ static int update(UPDATE_FUNC_ARGS) if (TYP(r)==PT_FIRE) { sim->kill_part(ID(r)); - if (RNG::Ref().chance(1, 30)) + if (sim->rng.chance(1, 30)) { sim->kill_part(i); return 1; } } - else if ((TYP(r)==PT_WATR || TYP(r)==PT_DSTW) && RNG::Ref().chance(1, 50)) + else if ((TYP(r)==PT_WATR || TYP(r)==PT_DSTW) && sim->rng.chance(1, 50)) { sim->part_change_type(ID(r), x+rx, y+ry, PT_CBNW); if (parts[i].ctype==5) //conserve number of water particles - ctype=5 means this CO2 hasn't released the water particle from BUBW yet @@ -88,14 +88,14 @@ static int update(UPDATE_FUNC_ARGS) } if (parts[i].temp > 9773.15 && sim->pv[y/CELL][x/CELL] > 200.0f) { - if (RNG::Ref().chance(1, 5)) + if (sim->rng.chance(1, 5)) { int j; sim->create_part(i,x,y,PT_O2); j = sim->create_part(-3,x,y,PT_NEUT); if (j != -1) parts[j].temp = MAX_TEMP; - if (RNG::Ref().chance(1, 50)) + if (sim->rng.chance(1, 50)) { j = sim->create_part(-3,x,y,PT_ELEC); if (j != -1) diff --git a/src/simulation/elements/COAL.cpp b/src/simulation/elements/COAL.cpp index bcb3cc755..ced74e46b 100644 --- a/src/simulation/elements/COAL.cpp +++ b/src/simulation/elements/COAL.cpp @@ -7,7 +7,7 @@ void Element::Element_COAL() { Identifier = "DEFAULT_PT_COAL"; Name = "COAL"; - Colour = PIXPACK(0x222222); + Colour = 0x222222_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -57,7 +57,7 @@ int Element_COAL_update(UPDATE_FUNC_ARGS) return 1; } else if (parts[i].life < 100) { parts[i].life--; - sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), PT_FIRE); + sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), PT_FIRE); } if (parts[i].type == PT_COAL) { @@ -96,8 +96,8 @@ int Element_COAL_graphics(GRAPHICS_FUNC_ARGS) auto q = int((cpart->temp > 595.15f) ? 200.0f : cpart->temp - 395.15f); *colr += int(sin(FREQUENCY*q) * 226); - *colg += int(sin(FREQUENCY*q*4.55 + 3.14) * 34); - *colb += int(sin(FREQUENCY*q*2.22 + 3.14) * 64); + *colg += int(sin(FREQUENCY*q*4.55 + TPT_PI_DBL) * 34); + *colb += int(sin(FREQUENCY*q*2.22 + TPT_PI_DBL) * 64); } return 0; } diff --git a/src/simulation/elements/CONV.cpp b/src/simulation/elements/CONV.cpp index 2795f06eb..292fc49ab 100644 --- a/src/simulation/elements/CONV.cpp +++ b/src/simulation/elements/CONV.cpp @@ -6,7 +6,7 @@ void Element::Element_CONV() { Identifier = "DEFAULT_PT_CONV"; Name = "CONV"; - Colour = PIXPACK(0x0AAB0A); + Colour = 0x0AAB0A_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -32,6 +32,7 @@ void Element::Element_CONV() Description = "Converter. Converts everything into whatever it first touches."; Properties = TYPE_SOLID | PROP_NOCTYPEDRAW; + CarriesTypeIn = (1U << FIELD_CTYPE) | (1U << FIELD_TMP); LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/CRAY.cpp b/src/simulation/elements/CRAY.cpp index 5e8ae6d2a..e68f9b31b 100644 --- a/src/simulation/elements/CRAY.cpp +++ b/src/simulation/elements/CRAY.cpp @@ -8,7 +8,7 @@ void Element::Element_CRAY() { Identifier = "DEFAULT_PT_CRAY"; Name = "CRAY"; - Colour = PIXPACK(0xBBFF00); + Colour = 0xBBFF00_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; @@ -34,6 +34,7 @@ void Element::Element_CRAY() Description = "Particle Ray Emitter. Creates a beam of particles set by its ctype, with a range set by tmp."; Properties = TYPE_SOLID; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/CRMC.cpp b/src/simulation/elements/CRMC.cpp index 517e07e3c..dc2ae7fa8 100644 --- a/src/simulation/elements/CRMC.cpp +++ b/src/simulation/elements/CRMC.cpp @@ -8,7 +8,7 @@ void Element::Element_CRMC() { Identifier = "DEFAULT_PT_CRMC"; Name = "CRMC"; - Colour = PIXPACK(0xD6D1D4); + Colour = 0xD6D1D4_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -67,5 +67,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].tmp2 = RNG::Ref().between(0, 4); + sim->parts[i].tmp2 = sim->rng.between(0, 4); } diff --git a/src/simulation/elements/DCEL.cpp b/src/simulation/elements/DCEL.cpp index f23a52740..fb13fe891 100644 --- a/src/simulation/elements/DCEL.cpp +++ b/src/simulation/elements/DCEL.cpp @@ -7,7 +7,7 @@ void Element::Element_DCEL() { Identifier = "DEFAULT_PT_DCEL"; Name = "DCEL"; - Colour = PIXPACK(0x99CC00); + Colour = 0x99CC00_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; diff --git a/src/simulation/elements/DESL.cpp b/src/simulation/elements/DESL.cpp index bf3eee9d6..65789ffd4 100644 --- a/src/simulation/elements/DESL.cpp +++ b/src/simulation/elements/DESL.cpp @@ -4,7 +4,7 @@ void Element::Element_DESL() { Identifier = "DEFAULT_PT_DESL"; Name = "DESL"; - Colour = PIXPACK(0x440000); + Colour = 0x440000_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/DEST.cpp b/src/simulation/elements/DEST.cpp index f7331e616..1f09a87db 100644 --- a/src/simulation/elements/DEST.cpp +++ b/src/simulation/elements/DEST.cpp @@ -7,7 +7,7 @@ void Element::Element_DEST() { Identifier = "DEFAULT_PT_DEST"; Name = "DEST"; - Colour = PIXPACK(0xFF3311); + Colour = 0xFF3311_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -49,8 +49,8 @@ void Element::Element_DEST() static int update(UPDATE_FUNC_ARGS) { - int rx = RNG::Ref().between(-2, 2); - int ry = RNG::Ref().between(-2, 2); + int rx = sim->rng.between(-2, 2); + int ry = sim->rng.between(-2, 2); int r = pmap[y+ry][x+rx]; if (!r) return 0; @@ -60,13 +60,13 @@ static int update(UPDATE_FUNC_ARGS) if (parts[i].life<=0 || parts[i].life>37) { - parts[i].life = RNG::Ref().between(30, 49); + parts[i].life = sim->rng.between(30, 49); sim->pv[y/CELL][x/CELL]+=60.0f; } if (rt == PT_PLUT || rt == PT_DEUT) { sim->pv[y/CELL][x/CELL]+=20.0f; - if (RNG::Ref().chance(1, 2)) + if (sim->rng.chance(1, 2)) { sim->create_part(ID(r), x+rx, y+ry, PT_NEUT); parts[ID(r)].temp = MAX_TEMP; @@ -78,7 +78,7 @@ static int update(UPDATE_FUNC_ARGS) { sim->create_part(ID(r), x+rx, y+ry, PT_PLSM); } - else if (RNG::Ref().chance(1, 3)) + else if (sim->rng.chance(1, 3)) { sim->kill_part(ID(r)); parts[i].life -= 4*((sim->elements[rt].Properties&TYPE_SOLID)?3:1); diff --git a/src/simulation/elements/DEUT.cpp b/src/simulation/elements/DEUT.cpp index 60d51aa3d..969488d57 100644 --- a/src/simulation/elements/DEUT.cpp +++ b/src/simulation/elements/DEUT.cpp @@ -8,7 +8,7 @@ void Element::Element_DEUT() { Identifier = "DEFAULT_PT_DEUT"; Name = "DEUT"; - Colour = PIXPACK(0x00153F); + Colour = 0x00153F_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -54,11 +54,11 @@ void Element::Element_DEUT() static int update(UPDATE_FUNC_ARGS) { int r, rx, ry, trade, np; - float gravtot = fabs(sim->gravy[(y/CELL)*(XRES/CELL)+(x/CELL)])+fabs(sim->gravx[(y/CELL)*(XRES/CELL)+(x/CELL)]); + float gravtot = fabs(sim->gravy[(y/CELL)*XCELLS+(x/CELL)])+fabs(sim->gravx[(y/CELL)*XCELLS+(x/CELL)]); // Prevent division by 0 float temp = std::max(1.0f, (parts[i].temp + 1)); auto maxlife = int(((10000/(temp + 1))-1)); - if (RNG::Ref().chance(10000 % static_cast(temp + 1), static_cast(temp + 1))) + if (sim->rng.chance(10000 % static_cast(temp + 1), static_cast(temp + 1))) maxlife++; // Compress when Newtonian gravity is applied // multiplier=1 when gravtot=0, multiplier -> 5 as gravtot -> inf @@ -72,7 +72,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r || (parts[i].life >=maxlife)) continue; - if (TYP(r)==PT_DEUT&& RNG::Ref().chance(1, 3)) + if (TYP(r)==PT_DEUT&& sim->rng.chance(1, 3)) { // If neighbour life+1 fits in the free capacity for this particle, absorb neighbour // Condition is written in this way so that large neighbour life values don't cause integer overflow @@ -105,8 +105,8 @@ static int update(UPDATE_FUNC_ARGS) trade: for ( trade = 0; trade<4; trade ++) { - rx = RNG::Ref().between(-2, 2); - ry = RNG::Ref().between(-2, 2); + rx = sim->rng.between(-2, 2); + ry = sim->rng.between(-2, 2); if (BOUNDS_CHECK && (rx || ry)) { r = pmap[y+ry][x+rx]; diff --git a/src/simulation/elements/DLAY.cpp b/src/simulation/elements/DLAY.cpp index ea7f73efe..2f280f7fb 100644 --- a/src/simulation/elements/DLAY.cpp +++ b/src/simulation/elements/DLAY.cpp @@ -7,7 +7,7 @@ void Element::Element_DLAY() { Identifier = "DEFAULT_PT_DLAY"; Name = "DLAY"; - Colour = PIXPACK(0x753590); + Colour = 0x753590_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; diff --git a/src/simulation/elements/DMG.cpp b/src/simulation/elements/DMG.cpp index d550d47e7..37c29e186 100644 --- a/src/simulation/elements/DMG.cpp +++ b/src/simulation/elements/DMG.cpp @@ -7,7 +7,7 @@ void Element::Element_DMG() { Identifier = "DEFAULT_PT_DMG"; Name = "DMG"; - Colour = PIXPACK(0x88FF88); + Colour = 0x88FF88_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; diff --git a/src/simulation/elements/DMND.cpp b/src/simulation/elements/DMND.cpp index 291e9d44e..b5a8438c1 100644 --- a/src/simulation/elements/DMND.cpp +++ b/src/simulation/elements/DMND.cpp @@ -4,7 +4,7 @@ void Element::Element_DMND() { Identifier = "DEFAULT_PT_DMND"; Name = "DMND"; - Colour = PIXPACK(0xCCFFFF); + Colour = 0xCCFFFF_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; diff --git a/src/simulation/elements/DRAY.cpp b/src/simulation/elements/DRAY.cpp index 12a48c08e..78db8e0f6 100644 --- a/src/simulation/elements/DRAY.cpp +++ b/src/simulation/elements/DRAY.cpp @@ -6,7 +6,7 @@ void Element::Element_DRAY() { Identifier = "DEFAULT_PT_DRAY"; Name = "DRAY"; - Colour = PIXPACK(0xFFAA22); + Colour = 0xFFAA22_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; @@ -32,6 +32,7 @@ void Element::Element_DRAY() Description = "Duplicator ray. Replicates a line of particles in front of it."; Properties = TYPE_SOLID; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/DRIC.cpp b/src/simulation/elements/DRIC.cpp index 18e855d8a..90cb36b9c 100644 --- a/src/simulation/elements/DRIC.cpp +++ b/src/simulation/elements/DRIC.cpp @@ -4,7 +4,7 @@ void Element::Element_DRIC() { Identifier = "DEFAULT_PT_DRIC"; Name = "DRIC"; - Colour = PIXPACK(0xE0E0E0); + Colour = 0xE0E0E0_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/DSTW.cpp b/src/simulation/elements/DSTW.cpp index b557fbf41..b5d8d4b41 100644 --- a/src/simulation/elements/DSTW.cpp +++ b/src/simulation/elements/DSTW.cpp @@ -6,7 +6,7 @@ void Element::Element_DSTW() { Identifier = "DEFAULT_PT_DSTW"; Name = "DSTW"; - Colour = PIXPACK(0x1020C0); + Colour = 0x1020C0_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -57,29 +57,29 @@ static int update(UPDATE_FUNC_ARGS) switch (TYP(r)) { case PT_SALT: - if (RNG::Ref().chance(1, 50)) + if (sim->rng.chance(1, 50)) { sim->part_change_type(i,x,y,PT_SLTW); // on average, convert 3 DSTW to SLTW before SALT turns into SLTW - if (RNG::Ref().chance(1, 3)) + if (sim->rng.chance(1, 3)) sim->part_change_type(ID(r),x+rx,y+ry,PT_SLTW); } break; case PT_SLTW: - if (RNG::Ref().chance(1, 2000)) + if (sim->rng.chance(1, 2000)) { sim->part_change_type(i,x,y,PT_SLTW); break; } case PT_WATR: - if (RNG::Ref().chance(1, 100)) + if (sim->rng.chance(1, 100)) { sim->part_change_type(i,x,y,PT_WATR); } break; case PT_RBDM: case PT_LRBD: - if ((sim->legacy_enable||parts[i].temp>12.0f) && RNG::Ref().chance(1, 100)) + if ((sim->legacy_enable||parts[i].temp>12.0f) && sim->rng.chance(1, 100)) { sim->part_change_type(i,x,y,PT_FIRE); parts[i].life = 4; @@ -87,7 +87,7 @@ static int update(UPDATE_FUNC_ARGS) break; case PT_FIRE: sim->kill_part(ID(r)); - if (RNG::Ref().chance(1, 30)) + if (sim->rng.chance(1, 30)) { sim->kill_part(i); return 1; diff --git a/src/simulation/elements/DTEC.cpp b/src/simulation/elements/DTEC.cpp index d6a847c9c..5f0f382cc 100644 --- a/src/simulation/elements/DTEC.cpp +++ b/src/simulation/elements/DTEC.cpp @@ -6,7 +6,7 @@ void Element::Element_DTEC() { Identifier = "DEFAULT_PT_DTEC"; Name = "DTEC"; - Colour = PIXPACK(0xFD9D18); + Colour = 0xFD9D18_rgb; MenuVisible = 1; MenuSection = SC_SENSOR; Enabled = 1; @@ -32,6 +32,7 @@ void Element::Element_DTEC() Description = "Detector, creates a spark when something with its ctype is nearby."; Properties = TYPE_SOLID; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -87,7 +88,7 @@ static int update(UPDATE_FUNC_ARGS) continue; if (TYP(r) == parts[i].ctype && (parts[i].ctype != PT_LIFE || parts[i].tmp == parts[ID(r)].ctype || !parts[i].tmp)) parts[i].life = 1; - if (TYP(r) == PT_PHOT || (TYP(r) == PT_BRAY && parts[ID(r)].tmp!=2)) + if (TYP(r) == PT_PHOT || (TYP(r) == PT_BRAY && parts[ID(r)].tmp!=2) || TYP(r) == PT_BIZR || TYP(r) == PT_BIZRG || TYP(r) == PT_BIZRS) { setFilt = true; photonWl = parts[ID(r)].ctype; diff --git a/src/simulation/elements/DUST.cpp b/src/simulation/elements/DUST.cpp index 090a3c491..fe87326e1 100644 --- a/src/simulation/elements/DUST.cpp +++ b/src/simulation/elements/DUST.cpp @@ -4,7 +4,7 @@ void Element::Element_DUST() { Identifier = "DEFAULT_PT_DUST"; Name = "DUST"; - Colour = PIXPACK(0xFFE0A0); + Colour = 0xFFE0A0_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/DYST.cpp b/src/simulation/elements/DYST.cpp index 0530a1d47..10a7260a8 100644 --- a/src/simulation/elements/DYST.cpp +++ b/src/simulation/elements/DYST.cpp @@ -4,7 +4,7 @@ void Element::Element_DYST() { Identifier = "DEFAULT_PT_DYST"; Name = "DYST"; - Colour = PIXPACK(0xBBB0A0); + Colour = 0xBBB0A0_rgb; MenuVisible = 0; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/E116.cpp b/src/simulation/elements/E116.cpp index a05476e49..60aefce9f 100644 --- a/src/simulation/elements/E116.cpp +++ b/src/simulation/elements/E116.cpp @@ -4,7 +4,7 @@ void Element::Element_E116() { Identifier = "DEFAULT_PT_116"; Name = "EQVE"; - Colour = PIXPACK(0xFFE0A0); + Colour = 0xFFE0A0_rgb; MenuVisible = 0; MenuSection = SC_CRACKER2; Enabled = 1; diff --git a/src/simulation/elements/E146.cpp b/src/simulation/elements/E146.cpp index 315e2cc7b..6339f1b8d 100644 --- a/src/simulation/elements/E146.cpp +++ b/src/simulation/elements/E146.cpp @@ -4,7 +4,7 @@ void Element::Element_E146() { Identifier = "DEFAULT_PT_146"; Name = "BRAN"; - Colour = PIXPACK(0xCCCC00); + Colour = 0xCCCC00_rgb; MenuVisible = 0; MenuSection = SC_LIFE; Enabled = 0; diff --git a/src/simulation/elements/ELEC.cpp b/src/simulation/elements/ELEC.cpp index f8df72039..3c9ce1825 100644 --- a/src/simulation/elements/ELEC.cpp +++ b/src/simulation/elements/ELEC.cpp @@ -8,7 +8,7 @@ void Element::Element_ELEC() { Identifier = "DEFAULT_PT_ELEC"; Name = "ELEC"; - Colour = PIXPACK(0xDFEFFF); + Colour = 0xDFEFFF_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -73,20 +73,20 @@ static int update(UPDATE_FUNC_ARGS) parts[nb].tmp = 0; parts[nb].life = 50; parts[nb].temp = parts[i].temp*0.8f; - parts[nb].vx = float(RNG::Ref().between(-10, 10)); - parts[nb].vy = float(RNG::Ref().between(-10, 10)); + parts[nb].vx = float(sim->rng.between(-10, 10)); + parts[nb].vy = float(sim->rng.between(-10, 10)); } } sim->kill_part(i); return 1; case PT_LCRY: - parts[ID(r)].tmp2 = RNG::Ref().between(5, 9); + parts[ID(r)].tmp2 = sim->rng.between(5, 9); break; case PT_WATR: case PT_DSTW: case PT_SLTW: case PT_CBNW: - if (RNG::Ref().chance(1, 3)) + if (sim->rng.chance(1, 3)) sim->create_part(ID(r), x+rx, y+ry, PT_O2); else sim->create_part(ID(r), x+rx, y+ry, PT_H2); @@ -139,7 +139,7 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - float a = RNG::Ref().between(0, 359) * 3.14159f / 180.0f; + float a = sim->rng.between(0, 359) * 3.14159f / 180.0f; sim->parts[i].life = 680; sim->parts[i].vx = 2.0f * cosf(a); sim->parts[i].vy = 2.0f * sinf(a); diff --git a/src/simulation/elements/EMBR.cpp b/src/simulation/elements/EMBR.cpp index 2c7e256f4..8c0802f04 100644 --- a/src/simulation/elements/EMBR.cpp +++ b/src/simulation/elements/EMBR.cpp @@ -7,7 +7,7 @@ void Element::Element_EMBR() { Identifier = "DEFAULT_PT_EMBR"; Name = "EMBR"; - Colour = PIXPACK(0xFFF288); + Colour = 0xFFF288_rgb; MenuVisible = 0; MenuSection = SC_EXPLOSIVE; Enabled = 1; diff --git a/src/simulation/elements/EMP.cpp b/src/simulation/elements/EMP.cpp index 0a464097d..ed78e55c3 100644 --- a/src/simulation/elements/EMP.cpp +++ b/src/simulation/elements/EMP.cpp @@ -7,7 +7,7 @@ void Element::Element_EMP() { Identifier = "DEFAULT_PT_EMP"; Name = "EMP"; - Colour = PIXPACK(0x66AAFF); + Colour = 0x66AAFF_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; @@ -66,7 +66,7 @@ public: } void apply(Simulation *sim, Particle &p) { - p.temp = restrict_flt(p.temp+getDelta(RNG::Ref().uniform01()), MIN_TEMP, MAX_TEMP); + p.temp = restrict_flt(p.temp+getDelta(sim->rng.uniform01()), MIN_TEMP, MAX_TEMP); } }; @@ -118,9 +118,9 @@ void Element_EMP_Trigger(Simulation *sim, int triggerCount) { is_elec = true; temp_center.apply(sim, parts[r]); - if (RNG::Ref().uniform01() < prob_changeCenter) + if (sim->rng.uniform01() < prob_changeCenter) { - if (RNG::Ref().chance(2, 5)) + if (sim->rng.chance(2, 5)) sim->part_change_type(r, rx, ry, PT_BREC); else sim->part_change_type(r, rx, ry, PT_NTCT); @@ -143,10 +143,10 @@ void Element_EMP_Trigger(Simulation *sim, int triggerCount) { case PT_METL: temp_metal.apply(sim, parts[n]); - if (RNG::Ref().uniform01() < prob_breakMETL) + if (sim->rng.uniform01() < prob_breakMETL) { sim->part_change_type(n, rx+nx, ry+ny, PT_BMTL); - if (RNG::Ref().uniform01() < prob_breakMETLMore) + if (sim->rng.uniform01() < prob_breakMETLMore) { sim->part_change_type(n, rx+nx, ry+ny, PT_BRMT); parts[n].temp = restrict_flt(parts[n].temp+1000.0f, MIN_TEMP, MAX_TEMP); @@ -155,19 +155,19 @@ void Element_EMP_Trigger(Simulation *sim, int triggerCount) break; case PT_BMTL: temp_metal.apply(sim, parts[n]); - if (RNG::Ref().uniform01() < prob_breakBMTL) + if (sim->rng.uniform01() < prob_breakBMTL) { sim->part_change_type(n, rx+nx, ry+ny, PT_BRMT); parts[n].temp = restrict_flt(parts[n].temp+1000.0f, MIN_TEMP, MAX_TEMP); } break; case PT_WIFI: - if (RNG::Ref().uniform01() < prob_randWIFI) + if (sim->rng.uniform01() < prob_randWIFI) { // Randomize channel - parts[n].temp = float(RNG::Ref().between(0, MAX_TEMP-1)); + parts[n].temp = float(sim->rng.between(0, int(MAX_TEMP)-1)); } - if (RNG::Ref().uniform01() < prob_breakWIFI) + if (sim->rng.uniform01() < prob_breakWIFI) { sim->create_part(n, rx+nx, ry+ny, PT_BREC); parts[n].temp = restrict_flt(parts[n].temp+1000.0f, MIN_TEMP, MAX_TEMP); @@ -180,22 +180,22 @@ void Element_EMP_Trigger(Simulation *sim, int triggerCount) switch (ntype) { case PT_SWCH: - if (RNG::Ref().uniform01() < prob_breakSWCH) + if (sim->rng.uniform01() < prob_breakSWCH) sim->part_change_type(n, rx+nx, ry+ny, PT_BREC); temp_SWCH.apply(sim, parts[n]); break; case PT_ARAY: - if (RNG::Ref().uniform01() < prob_breakARAY) + if (sim->rng.uniform01() < prob_breakARAY) { sim->create_part(n, rx+nx, ry+ny, PT_BREC); parts[n].temp = restrict_flt(parts[n].temp+1000.0f, MIN_TEMP, MAX_TEMP); } break; case PT_DLAY: - if (RNG::Ref().uniform01() < prob_randDLAY) + if (sim->rng.uniform01() < prob_randDLAY) { // Randomize delay - parts[n].temp = RNG::Ref().between(0, 255) + 273.15f; + parts[n].temp = sim->rng.between(0, 255) + 273.15f; } break; default: diff --git a/src/simulation/elements/ETRD.cpp b/src/simulation/elements/ETRD.cpp index f7114dbf6..5fc3089fd 100644 --- a/src/simulation/elements/ETRD.cpp +++ b/src/simulation/elements/ETRD.cpp @@ -1,5 +1,5 @@ -#include #include "simulation/ElementCommon.h" +#include static void initDeltaPos(); static void changeType(ELEMENT_CHANGETYPE_FUNC_ARGS); @@ -8,7 +8,7 @@ void Element::Element_ETRD() { Identifier = "DEFAULT_PT_ETRD"; Name = "ETRD"; - Colour = PIXPACK(0x404040); + Colour = 0x404040_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/EXOT.cpp b/src/simulation/elements/EXOT.cpp index 331250906..f8ff425dc 100644 --- a/src/simulation/elements/EXOT.cpp +++ b/src/simulation/elements/EXOT.cpp @@ -7,7 +7,7 @@ void Element::Element_EXOT() { Identifier = "DEFAULT_PT_EXOT"; Name = "EXOT"; - Colour = PIXPACK(0x247BFE); + Colour = 0x247BFE_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -64,7 +64,7 @@ static int update(UPDATE_FUNC_ARGS) rt = TYP(r); if (rt == PT_WARP) { - if (parts[ID(r)].tmp2>2000 && RNG::Ref().chance(1, 100)) + if (parts[ID(r)].tmp2>2000 && sim->rng.chance(1, 100)) { parts[i].tmp2 += 100; } @@ -73,7 +73,7 @@ static int update(UPDATE_FUNC_ARGS) { if (parts[ID(r)].ctype == PT_PROT) parts[i].ctype = PT_PROT; - if (parts[ID(r)].life == 1500 && RNG::Ref().chance(1, 1000)) + if (parts[ID(r)].life == 1500 && sim->rng.chance(1, 1000)) parts[i].life = 1500; } else if (rt == PT_LAVA) @@ -81,7 +81,7 @@ static int update(UPDATE_FUNC_ARGS) //turn molten TTAN or molten GOLD to molten VIBR if (parts[ID(r)].ctype == PT_TTAN || parts[ID(r)].ctype == PT_GOLD) { - if (RNG::Ref().chance(1, 10)) + if (sim->rng.chance(1, 10)) { parts[ID(r)].ctype = PT_VIBR; sim->kill_part(i); @@ -91,7 +91,7 @@ static int update(UPDATE_FUNC_ARGS) //molten VIBR will kill the leftover EXOT though, so the VIBR isn't killed later else if (parts[ID(r)].ctype == PT_VIBR) { - if (RNG::Ref().chance(1, 1000)) + if (sim->rng.chance(1, 1000)) { sim->kill_part(i); return 1; @@ -138,8 +138,8 @@ static int update(UPDATE_FUNC_ARGS) { for (trade = 0; trade < 9; trade++) { - rx = RNG::Ref().between(-2, 2); - ry = RNG::Ref().between(-2, 2); + rx = sim->rng.between(-2, 2); + ry = sim->rng.between(-2, 2); if (BOUNDS_CHECK && (rx || ry)) { r = pmap[y+ry][x+rx]; @@ -193,7 +193,7 @@ static int graphics(GRAPHICS_FUNC_ARGS) auto c = cpart->tmp2; if (cpart->life < 1001) { - if (RNG::Ref().chance(cpart->tmp2 - 1, 1000)) + if (ren->rng.chance(cpart->tmp2 - 1, 1000)) { float frequency = 0.04045f; *colr = int(sin(frequency*c + 4) * 127 + 150); diff --git a/src/simulation/elements/FIGH.cpp b/src/simulation/elements/FIGH.cpp index f2d0b2c4a..fc82caee3 100644 --- a/src/simulation/elements/FIGH.cpp +++ b/src/simulation/elements/FIGH.cpp @@ -15,7 +15,7 @@ void Element::Element_FIGH() { Identifier = "DEFAULT_PT_FIGH"; Name = "FIGH"; - Colour = PIXPACK(0xFFE0A0); + Colour = 0xFFE0A0_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -43,6 +43,7 @@ void Element::Element_FIGH() Description = "Fighter. Tries to kill stickmen. You must first give it an element to kill him with."; Properties = PROP_NOCTYPEDRAW; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -70,7 +71,7 @@ static int update(UPDATE_FUNC_ARGS) } playerst* figh = &sim->fighters[(unsigned char)parts[i].tmp]; - int tarx, tary; + int tarx = 0, tary = 0; parts[i].tmp2 = 0; //0 - stay in place, 1 - seek a stick man diff --git a/src/simulation/elements/FILT.cpp b/src/simulation/elements/FILT.cpp index c377396db..d3b3209de 100644 --- a/src/simulation/elements/FILT.cpp +++ b/src/simulation/elements/FILT.cpp @@ -2,14 +2,14 @@ static int graphics(GRAPHICS_FUNC_ARGS); static void create(ELEMENT_CREATE_FUNC_ARGS); -int Element_FILT_interactWavelengths(Particle* cpart, int origWl); +int Element_FILT_interactWavelengths(Simulation *sim, Particle* cpart, int origWl); int Element_FILT_getWavelengths(Particle* cpart); void Element::Element_FILT() { Identifier = "DEFAULT_PT_FILT"; Name = "FILT"; - Colour = PIXPACK(0x000056); + Colour = 0x000056_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -81,7 +81,7 @@ static void create(ELEMENT_CREATE_FUNC_ARGS) // Returns the wavelengths in a particle after FILT interacts with it (e.g. a photon) // cpart is the FILT particle, origWl the original wavelengths in the interacting particle -int Element_FILT_interactWavelengths(Particle* cpart, int origWl) +int Element_FILT_interactWavelengths(Simulation *sim, Particle* cpart, int origWl) { const int mask = 0x3FFFFFFF; int filtWl = Element_FILT_getWavelengths(cpart); @@ -115,9 +115,9 @@ int Element_FILT_interactWavelengths(Particle* cpart, int origWl) return (~origWl) & mask; // Invert colours case 9: { - int t1 = (origWl & 0x0000FF) + RNG::Ref().between(-2, 2); - int t2 = ((origWl & 0x00FF00)>>8) + RNG::Ref().between(-2, 2); - int t3 = ((origWl & 0xFF0000)>>16) + RNG::Ref().between(-2, 2); + int t1 = (origWl & 0x0000FF) + sim->rng.between(-2, 2); + int t2 = ((origWl & 0x00FF00)>>8) + sim->rng.between(-2, 2); + int t3 = ((origWl & 0xFF0000)>>16) + sim->rng.between(-2, 2); return (origWl & 0xFF000000) | (t3<<16) | (t2<<8) | t1; } case 10: diff --git a/src/simulation/elements/FIRE.cpp b/src/simulation/elements/FIRE.cpp index 8e62a1c2a..b3df41eac 100644 --- a/src/simulation/elements/FIRE.cpp +++ b/src/simulation/elements/FIRE.cpp @@ -10,7 +10,7 @@ void Element::Element_FIRE() { Identifier = "DEFAULT_PT_FIRE"; Name = "FIRE"; - Colour = PIXPACK(0xFF1000); + Colour = 0xFF1000_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -83,7 +83,7 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) else if (parts[i].temp<625) { sim->part_change_type(i,x,y,PT_SMKE); - parts[i].life = RNG::Ref().between(250, 269); + parts[i].life = sim->rng.between(250, 269); } } break; @@ -97,34 +97,34 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) break; } - if (pres >= 25 && RNG::Ref().chance(1, 12500)) + if (pres >= 25 && sim->rng.chance(1, 12500)) { if (pres <= 50) { - if (RNG::Ref().chance(1, 2)) + if (sim->rng.chance(1, 2)) parts[i].ctype = PT_BRMT; else parts[i].ctype = PT_CNCT; } else if (pres <= 75) { - if (pres >= 73 || RNG::Ref().chance(1, 8)) + if (pres >= 73 || sim->rng.chance(1, 8)) parts[i].ctype = PT_GOLD; else parts[i].ctype = PT_QRTZ; } else if (pres <= 100 && parts[i].temp >= 5000) { - if (RNG::Ref().chance(1, 5)) // 1 in 5 chance IRON to TTAN + if (sim->rng.chance(1, 5)) // 1 in 5 chance IRON to TTAN parts[i].ctype = PT_TTAN; else parts[i].ctype = PT_IRON; } - else if (parts[i].temp >= 5000 && RNG::Ref().chance(1, 5)) + else if (parts[i].temp >= 5000 && sim->rng.chance(1, 5)) { - if (RNG::Ref().chance(1, 5)) + if (sim->rng.chance(1, 5)) parts[i].ctype = PT_URAN; - else if (RNG::Ref().chance(1, 5)) + else if (sim->rng.chance(1, 5)) parts[i].ctype = PT_PLUT; else parts[i].ctype = PT_TUNG; @@ -133,7 +133,7 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) } else if ((parts[i].ctype == PT_STNE || !parts[i].ctype) && pres >= 30.0f && (parts[i].temp > sim->elements[PT_ROCK].HighTemperature || pres < sim->elements[PT_ROCK].HighPressure)) // Form ROCK with pressure, if it will stay molten or not immediately break { - parts[i].tmp2 = RNG::Ref().between(0, 10); // Provide tmp2 for color noise + parts[i].tmp2 = sim->rng.between(0, 10); // Provide tmp2 for color noise parts[i].ctype = PT_ROCK; } break; @@ -153,7 +153,7 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) //THRM burning if (rt==PT_THRM && (t==PT_FIRE || t==PT_PLSM || t==PT_LAVA)) { - if (RNG::Ref().chance(1, 500)) { + if (sim->rng.chance(1, 500)) { sim->part_change_type(ID(r),x+rx,y+ry,PT_LAVA); parts[ID(r)].ctype = PT_BMTL; parts[ID(r)].temp = 3500.0f; @@ -172,20 +172,20 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) { if ((t==PT_FIRE || t==PT_PLSM)) { - if (parts[ID(r)].life>100 && RNG::Ref().chance(1, 500)) + if (parts[ID(r)].life>100 && sim->rng.chance(1, 500)) { parts[ID(r)].life = 99; } } else if (t==PT_LAVA) { - if (parts[i].ctype == PT_IRON && RNG::Ref().chance(1, 500)) + if (parts[i].ctype == PT_IRON && sim->rng.chance(1, 500)) { parts[i].ctype = PT_METL; sim->kill_part(ID(r)); continue; } - if ((parts[i].ctype == PT_STNE || parts[i].ctype == PT_NONE) && RNG::Ref().chance(1, 60)) + if ((parts[i].ctype == PT_STNE || parts[i].ctype == PT_NONE) && sim->rng.chance(1, 60)) { parts[i].ctype = PT_SLCN; sim->kill_part(ID(r)); @@ -208,7 +208,7 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) } else if (rt == PT_O2 && parts[i].ctype == PT_SLCN) { - switch (RNG::Ref().between(0, 2)) + switch (sim->rng.between(0, 2)) { case 0: parts[i].ctype = PT_SAND; @@ -246,7 +246,7 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) } } else if (parts[i].ctype == PT_ROCK && rt == PT_LAVA && parts[ID(r)].ctype == PT_GOLD && parts[ID(r)].tmp == 0 && - sim->pv[y / CELL][x / CELL] >= 50 && RNG::Ref().chance(1, 10000)) // Produce GOLD veins/clusters + sim->pv[y / CELL][x / CELL] >= 50 && sim->rng.chance(1, 10000)) // Produce GOLD veins/clusters { parts[i].ctype = PT_GOLD; if (rx > 1 || rx < -1) // Trend veins vertical @@ -259,7 +259,7 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) } if ((surround_space || sim->elements[rt].Explosive) && - sim->elements[rt].Flammable && RNG::Ref().chance(int(sim->elements[rt].Flammable + (sim->pv[(y+ry)/CELL][(x+rx)/CELL] * 10.0f)), 1000) && + sim->elements[rt].Flammable && sim->rng.chance(int(sim->elements[rt].Flammable + (sim->pv[(y+ry)/CELL][(x+rx)/CELL] * 10.0f)), 1000) && //exceptions, t is the thing causing the spark and rt is what's burning (t != PT_SPRK || (rt != PT_RBDM && rt != PT_LRBD && rt != PT_INSL)) && (t != PT_PHOT || rt != PT_INSL) && @@ -267,7 +267,7 @@ int Element_FIRE_update(UPDATE_FUNC_ARGS) { sim->part_change_type(ID(r), x+rx, y+ry, PT_FIRE); parts[ID(r)].temp = restrict_flt(sim->elements[PT_FIRE].DefaultProperties.temp + (sim->elements[rt].Flammable/2), MIN_TEMP, MAX_TEMP); - parts[ID(r)].life = RNG::Ref().between(180, 259); + parts[ID(r)].life = sim->rng.between(180, 259); parts[ID(r)].tmp = parts[ID(r)].ctype = 0; if (sim->elements[rt].Explosive) sim->pv[y/CELL][x/CELL] += 0.25f * CFDS; @@ -297,7 +297,7 @@ static int updateLegacy(UPDATE_FUNC_ARGS) if (sim->elements[rt].Meltable && ((rt!=PT_RBDM && rt!=PT_LRBD) || t!=PT_SPRK) && ((t!=PT_FIRE&&t!=PT_PLSM) || (rt!=PT_METL && rt!=PT_IRON && rt!=PT_ETRD && rt!=PT_PSCN && rt!=PT_NSCN && rt!=PT_NTCT && rt!=PT_PTCT && rt!=PT_BMTL && rt!=PT_BRMT && rt!=PT_SALT && rt!=PT_INWR)) - && RNG::Ref().chance(sim->elements[rt].Meltable*lpv, 1000)) + && sim->rng.chance(sim->elements[rt].Meltable*lpv, 1000)) { if (t!=PT_LAVA || parts[i].life>0) { @@ -308,7 +308,7 @@ static int updateLegacy(UPDATE_FUNC_ARGS) else parts[ID(r)].ctype = rt; sim->part_change_type(ID(r),x+rx,y+ry,PT_LAVA); - parts[ID(r)].life = RNG::Ref().between(240, 359); + parts[ID(r)].life = sim->rng.between(240, 359); } else { @@ -353,10 +353,10 @@ static int updateLegacy(UPDATE_FUNC_ARGS) static int graphics(GRAPHICS_FUNC_ARGS) { - auto color = Renderer::flameTableAt(cpart->life); - *colr = PIXR(color); - *colg = PIXG(color); - *colb = PIXB(color); + RGB color = Renderer::flameTableAt(cpart->life); + *colr = color.Red; + *colg = color.Green; + *colb = color.Blue; *firea = 255; *firer = *colr; @@ -371,5 +371,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].life = RNG::Ref().between(120, 169); + sim->parts[i].life = sim->rng.between(120, 169); } diff --git a/src/simulation/elements/FIRW.cpp b/src/simulation/elements/FIRW.cpp index f818a8208..e635cd819 100644 --- a/src/simulation/elements/FIRW.cpp +++ b/src/simulation/elements/FIRW.cpp @@ -7,7 +7,7 @@ void Element::Element_FIRW() { Identifier = "DEFAULT_PT_FIRW"; Name = "FIRW"; - Colour = PIXPACK(0xFFA040); + Colour = 0xFFA040_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -65,12 +65,12 @@ static int update(UPDATE_FUNC_ARGS) sim->GetGravityField(x, y, sim->elements[PT_FIRW].Gravity, 1.0f, gx, gy); if (gx*gx+gy*gy < 0.001f) { - float angle = RNG::Ref().between(0, 6283) * 0.001f;//(in radians, between 0 and 2*pi) + float angle = sim->rng.between(0, 6283) * 0.001f;//(in radians, between 0 and 2*pi) gx += sinf(angle)*sim->elements[PT_FIRW].Gravity*0.5f; gy += cosf(angle)*sim->elements[PT_FIRW].Gravity*0.5f; } parts[i].tmp = 1; - parts[i].life = RNG::Ref().between(20, 29); + parts[i].life = sim->rng.between(20, 29); multiplier = (parts[i].life+20)*0.2f/sqrtf(gx*gx+gy*gy); parts[i].vx -= gx*multiplier; parts[i].vy -= gy*multiplier; @@ -87,20 +87,20 @@ static int update(UPDATE_FUNC_ARGS) } else //if (parts[i].tmp>=2) { - unsigned col = Renderer::firwTableAt(RNG::Ref().between(0, 199)); + unsigned col = Renderer::firwTableAt(sim->rng.between(0, 199)).Pack(); for (int n=0; n<40; n++) { np = sim->create_part(-3, x, y, PT_EMBR); if (np>-1) { - auto magnitude = RNG::Ref().between(40, 99) * 0.05f; - auto angle = RNG::Ref().between(0, 6283) * 0.001f;//(in radians, between 0 and 2*pi) + auto magnitude = sim->rng.between(40, 99) * 0.05f; + auto angle = sim->rng.between(0, 6283) * 0.001f;//(in radians, between 0 and 2*pi) parts[np].vx = parts[i].vx*0.5f + cosf(angle)*magnitude; parts[np].vy = parts[i].vy*0.5f + sinf(angle)*magnitude; parts[np].ctype = col; parts[np].tmp = 1; - parts[np].life = RNG::Ref().between(70, 109); - parts[np].temp = float(RNG::Ref().between(5750, 6249)); + parts[np].life = sim->rng.between(70, 109); + parts[np].temp = float(sim->rng.between(5750, 6249)); parts[np].dcolour = parts[i].dcolour; } } diff --git a/src/simulation/elements/FOG.cpp b/src/simulation/elements/FOG.cpp index 9b655e1f7..7cc87d951 100644 --- a/src/simulation/elements/FOG.cpp +++ b/src/simulation/elements/FOG.cpp @@ -6,7 +6,7 @@ void Element::Element_FOG() { Identifier = "DEFAULT_PT_FOG"; Name = "FOG"; - Colour = PIXPACK(0xAAAAAA); + Colour = 0xAAAAAA_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -56,13 +56,13 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if ((sim->elements[TYP(r)].Properties&TYPE_SOLID) && RNG::Ref().chance(1, 10) && parts[i].life==0 && !(TYP(r)==PT_CLNE || TYP(r)==PT_PCLN)) // TODO: should this also exclude BCLN? + if ((sim->elements[TYP(r)].Properties&TYPE_SOLID) && sim->rng.chance(1, 10) && parts[i].life==0 && !(TYP(r)==PT_CLNE || TYP(r)==PT_PCLN)) // TODO: should this also exclude BCLN? { sim->part_change_type(i,x,y,PT_RIME); } if (TYP(r)==PT_SPRK) { - parts[i].life += RNG::Ref().between(0, 19); + parts[i].life += sim->rng.between(0, 19); } } return 0; diff --git a/src/simulation/elements/FRAY.cpp b/src/simulation/elements/FRAY.cpp index 61cf72b9c..f4dfc8a78 100644 --- a/src/simulation/elements/FRAY.cpp +++ b/src/simulation/elements/FRAY.cpp @@ -6,7 +6,7 @@ void Element::Element_FRAY() { Identifier = "DEFAULT_PT_FRAY"; Name = "FRAY"; - Colour = PIXPACK(0x00BBFF); + Colour = 0x00BBFF_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; diff --git a/src/simulation/elements/FRME.cpp b/src/simulation/elements/FRME.cpp index 0f09ced90..2b009faf4 100644 --- a/src/simulation/elements/FRME.cpp +++ b/src/simulation/elements/FRME.cpp @@ -6,7 +6,7 @@ void Element::Element_FRME() { Identifier = "DEFAULT_PT_FRME"; Name = "FRME"; - Colour = PIXPACK(0x999988); + Colour = 0x999988_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; diff --git a/src/simulation/elements/FRZW.cpp b/src/simulation/elements/FRZW.cpp index a5306bbae..e6f543841 100644 --- a/src/simulation/elements/FRZW.cpp +++ b/src/simulation/elements/FRZW.cpp @@ -6,7 +6,7 @@ void Element::Element_FRZW() { Identifier = "DEFAULT_PT_FRZW"; Name = "FRZW"; - Colour = PIXPACK(0x1020C0); + Colour = 0x1020C0_rgb; MenuVisible = 1; MenuSection = SC_CRACKER2; Enabled = 1; @@ -58,12 +58,12 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)==PT_WATR && RNG::Ref().chance(1, 14)) + if (TYP(r)==PT_WATR && sim->rng.chance(1, 14)) { sim->part_change_type(ID(r),x+rx,y+ry,PT_FRZW); } } - if ((parts[i].life==0 && RNG::Ref().chance(1, 192)) || RNG::Ref().chance(100-parts[i].life, 50000)) + if ((parts[i].life==0 && sim->rng.chance(1, 192)) || sim->rng.chance(100-parts[i].life, 50000)) { sim->part_change_type(i,x,y,PT_ICEI); parts[i].ctype=PT_FRZW; diff --git a/src/simulation/elements/FRZZ.cpp b/src/simulation/elements/FRZZ.cpp index 5cf71423b..00fa61b6d 100644 --- a/src/simulation/elements/FRZZ.cpp +++ b/src/simulation/elements/FRZZ.cpp @@ -6,7 +6,7 @@ void Element::Element_FRZZ() { Identifier = "DEFAULT_PT_FRZZ"; Name = "FRZZ"; - Colour = PIXPACK(0xC0E0FF); + Colour = 0xC0E0FF_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -56,7 +56,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)==PT_WATR && RNG::Ref().chance(1, 20)) + if (TYP(r)==PT_WATR && sim->rng.chance(1, 20)) { sim->part_change_type(ID(r),x+rx,y+ry,PT_FRZW); parts[ID(r)].life = 100; diff --git a/src/simulation/elements/FSEP.cpp b/src/simulation/elements/FSEP.cpp index e5d7b6b3b..e117c01d6 100644 --- a/src/simulation/elements/FSEP.cpp +++ b/src/simulation/elements/FSEP.cpp @@ -6,7 +6,7 @@ void Element::Element_FSEP() { Identifier = "DEFAULT_PT_FSEP"; Name = "FSEP"; - Colour = PIXPACK(0x63AD5F); + Colour = 0x63AD5F_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -58,8 +58,8 @@ static int update(UPDATE_FUNC_ARGS) } else if (parts[i].life < 40) { parts[i].life--; - if (RNG::Ref().chance(1, 10)) { - r = sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), PT_PLSM); + if (sim->rng.chance(1, 10)) { + r = sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), PT_PLSM); if (r>-1) parts[r].life = 50; } @@ -72,7 +72,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_SPRK || (parts[i].temp>=(273.15+400.0f))) && parts[i].life>40 && RNG::Ref().chance(1, 15)) + if ((TYP(r)==PT_SPRK || (parts[i].temp>=(273.15+400.0f))) && parts[i].life>40 && sim->rng.chance(1, 15)) { parts[i].life = 39; } diff --git a/src/simulation/elements/FUSE.cpp b/src/simulation/elements/FUSE.cpp index 4d00aecaa..6a29db774 100644 --- a/src/simulation/elements/FUSE.cpp +++ b/src/simulation/elements/FUSE.cpp @@ -6,7 +6,7 @@ void Element::Element_FUSE() { Identifier = "DEFAULT_PT_FUSE"; Name = "FUSE"; - Colour = PIXPACK(0x0A5706); + Colour = 0x0A5706_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -59,8 +59,8 @@ static int update(UPDATE_FUNC_ARGS) } else if (parts[i].life < 40) { parts[i].life--; - if (RNG::Ref().chance(1, 100)) { - r = sim->create_part(-1, x + RNG::Ref().chance(-1, 1), y + RNG::Ref().chance(-1, 1), PT_PLSM); + if (sim->rng.chance(1, 100)) { + r = sim->create_part(-1, x + sim->rng.chance(-1, 1), y + sim->rng.chance(-1, 1), PT_PLSM); if (r>-1) parts[r].life = 50; } @@ -81,7 +81,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)==PT_SPRK || (parts[i].temp>=(273.15+700.0f) && RNG::Ref().chance(1, 20))) + if (TYP(r)==PT_SPRK || (parts[i].temp>=(273.15+700.0f) && sim->rng.chance(1, 20))) { if (parts[i].life > 40) parts[i].life = 39; diff --git a/src/simulation/elements/FWRK.cpp b/src/simulation/elements/FWRK.cpp index 8f6f52396..6a444b666 100644 --- a/src/simulation/elements/FWRK.cpp +++ b/src/simulation/elements/FWRK.cpp @@ -7,7 +7,7 @@ void Element::Element_FWRK() { Identifier = "DEFAULT_PT_FWRK"; Name = "FWRK"; - Colour = PIXPACK(0x666666); + Colour = 0x666666_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -48,14 +48,14 @@ void Element::Element_FWRK() static int update(UPDATE_FUNC_ARGS) { - if (parts[i].life == 0 && ((surround_space && parts[i].temp>400 && RNG::Ref().chance(int(9+parts[i].temp/40), 100000)) || parts[i].ctype == PT_DUST)) + if (parts[i].life == 0 && ((surround_space && parts[i].temp>400 && sim->rng.chance(int(9+parts[i].temp/40), 100000)) || parts[i].ctype == PT_DUST)) { float gx, gy, multiplier, gmax; int randTmp; sim->GetGravityField(x, y, sim->elements[PT_FWRK].Gravity, 1.0f, gx, gy); if (gx*gx+gy*gy < 0.001f) { - float angle = RNG::Ref().between(0, 6283) * 0.001f;//(in radians, between 0 and 2*pi) + float angle = sim->rng.between(0, 6283) * 0.001f;//(in radians, between 0 and 2*pi) gx += sinf(angle)*sim->elements[PT_FWRK].Gravity*0.5f; gy += cosf(angle)*sim->elements[PT_FWRK].Gravity*0.5f; } @@ -65,15 +65,15 @@ static int update(UPDATE_FUNC_ARGS) multiplier = 15.0f/sqrtf(gx*gx+gy*gy); //Some variation in speed parallel to gravity direction - randTmp = RNG::Ref().between(-100, 100); + randTmp = sim->rng.between(-100, 100); gx += gx*randTmp*0.002f; gy += gy*randTmp*0.002f; //and a bit more variation in speed perpendicular to gravity direction - randTmp = RNG::Ref().between(-100, 100); + randTmp = sim->rng.between(-100, 100); gx += -gy*randTmp*0.005f; gy += gx*randTmp*0.005f; - parts[i].life = RNG::Ref().between(18, 27); + parts[i].life = sim->rng.between(18, 27); parts[i].ctype=0; parts[i].vx -= gx*multiplier; parts[i].vy -= gy*multiplier; @@ -82,9 +82,9 @@ static int update(UPDATE_FUNC_ARGS) } if (parts[i].life<3&&parts[i].life>0) { - int r = RNG::Ref().between(11, 255); - int g = RNG::Ref().between(11, 255); - int b = RNG::Ref().between(11, 255); + int r = sim->rng.between(11, 255); + int g = sim->rng.between(11, 255); + int b = sim->rng.between(11, 255); int n; float angle, magnitude; unsigned col = (r<<16) | (g<<8) | b; @@ -93,14 +93,14 @@ static int update(UPDATE_FUNC_ARGS) int np = sim->create_part(-3, x, y, PT_EMBR); if (np>-1) { - magnitude = RNG::Ref().between(40, 99) * 0.05f; - angle = RNG::Ref().between(0, 6283) * 0.001f;//(in radians, between 0 and 2*pi) + magnitude = sim->rng.between(40, 99) * 0.05f; + angle = sim->rng.between(0, 6283) * 0.001f;//(in radians, between 0 and 2*pi) parts[np].vx = parts[i].vx*0.5f + cosf(angle)*magnitude; parts[np].vy = parts[i].vy*0.5f + sinf(angle)*magnitude; parts[np].ctype = col; parts[np].tmp = 1; - parts[np].life = RNG::Ref().between(70, 109); - parts[np].temp = float(RNG::Ref().between(5750, 6249)); + parts[np].life = sim->rng.between(70, 109); + parts[np].temp = float(sim->rng.between(5750, 6249)); parts[np].dcolour = parts[i].dcolour; } } diff --git a/src/simulation/elements/GAS.cpp b/src/simulation/elements/GAS.cpp index 2c68e6039..0be47c378 100644 --- a/src/simulation/elements/GAS.cpp +++ b/src/simulation/elements/GAS.cpp @@ -4,7 +4,7 @@ void Element::Element_GAS() { Identifier = "DEFAULT_PT_GAS"; Name = "GAS"; - Colour = PIXPACK(0xE0FF20); + Colour = 0xE0FF20_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; diff --git a/src/simulation/elements/GBMB.cpp b/src/simulation/elements/GBMB.cpp index b99c9678e..0bf40f5d5 100644 --- a/src/simulation/elements/GBMB.cpp +++ b/src/simulation/elements/GBMB.cpp @@ -7,7 +7,7 @@ void Element::Element_GBMB() { Identifier = "DEFAULT_PT_GBMB"; Name = "GBMB"; - Colour = PIXPACK(0x1144BB); + Colour = 0x1144BB_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; @@ -72,9 +72,9 @@ static int update(UPDATE_FUNC_ARGS) } } if (parts[i].life>20) - sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = 20; + sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] = 20; else if (parts[i].life>=1) - sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = -80; + sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] = -80; return 0; } diff --git a/src/simulation/elements/GEL.cpp b/src/simulation/elements/GEL.cpp index aa0b8a7fc..262790a4b 100644 --- a/src/simulation/elements/GEL.cpp +++ b/src/simulation/elements/GEL.cpp @@ -7,7 +7,7 @@ void Element::Element_GEL() { Identifier = "DEFAULT_PT_GEL"; Name = "GEL"; - Colour = PIXPACK(0xFF9900); + Colour = 0xFF9900_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -72,31 +72,31 @@ static int update(UPDATE_FUNC_ARGS) case PT_WATR: case PT_DSTW: case PT_FRZW: - if (parts[i].tmp<100 && RNG::Ref().chance(500, absorbChanceDenom)) + if (parts[i].tmp<100 && sim->rng.chance(500, absorbChanceDenom)) { parts[i].tmp++; sim->kill_part(ID(r)); } break; case PT_PSTE: - if (parts[i].tmp<100 && RNG::Ref().chance(20, absorbChanceDenom)) + if (parts[i].tmp<100 && sim->rng.chance(20, absorbChanceDenom)) { parts[i].tmp++; sim->create_part(ID(r), x+rx, y+ry, PT_CLST); } break; case PT_SLTW: - if (parts[i].tmp<100 && RNG::Ref().chance(50, absorbChanceDenom)) + if (parts[i].tmp<100 && sim->rng.chance(50, absorbChanceDenom)) { parts[i].tmp++; - if (RNG::Ref().chance(3, 4)) + if (sim->rng.chance(3, 4)) sim->kill_part(ID(r)); else sim->part_change_type(ID(r), x+rx, y+ry, PT_SALT); } break; case PT_CBNW: - if (parts[i].tmp < 100 && RNG::Ref().chance(100, absorbChanceDenom)) + if (parts[i].tmp < 100 && sim->rng.chance(100, absorbChanceDenom)) { parts[i].tmp++; sim->part_change_type(ID(r), x+rx, y+ry, PT_CO2); diff --git a/src/simulation/elements/GLAS.cpp b/src/simulation/elements/GLAS.cpp index d80bb2d7a..3034d94da 100644 --- a/src/simulation/elements/GLAS.cpp +++ b/src/simulation/elements/GLAS.cpp @@ -7,7 +7,7 @@ void Element::Element_GLAS() { Identifier = "DEFAULT_PT_GLAS"; Name = "GLAS"; - Colour = PIXPACK(0x404040); + Colour = 0x404040_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/GLOW.cpp b/src/simulation/elements/GLOW.cpp index 686bbbdbe..b0dfd9bc3 100644 --- a/src/simulation/elements/GLOW.cpp +++ b/src/simulation/elements/GLOW.cpp @@ -7,7 +7,7 @@ void Element::Element_GLOW() { Identifier = "DEFAULT_PT_GLOW"; Name = "GLOW"; - Colour = PIXPACK(0x445464); + Colour = 0x445464_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -58,7 +58,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)==PT_WATR && RNG::Ref().chance(1, 400)) + if (TYP(r)==PT_WATR && sim->rng.chance(1, 400)) { sim->kill_part(i); sim->part_change_type(ID(r),x+rx,y+ry,PT_DEUT); @@ -87,7 +87,7 @@ static int graphics(GRAPHICS_FUNC_ARGS) *colg = int(restrict_flt(64.0f+cpart->ctype, 0, 255)); *colb = int(restrict_flt(64.0f+cpart->tmp, 0, 255)); - int rng = RNG::Ref().between(1, 32); // + int rng = ren->rng.between(1, 32); // if(((*colr) + (*colg) + (*colb)) > (256 + rng)) { *colr -= 54; *colg -= 54; diff --git a/src/simulation/elements/GOLD.cpp b/src/simulation/elements/GOLD.cpp index 9d14129be..38531a20e 100644 --- a/src/simulation/elements/GOLD.cpp +++ b/src/simulation/elements/GOLD.cpp @@ -8,7 +8,7 @@ void Element::Element_GOLD() { Identifier = "DEFAULT_PT_GOLD"; Name = "GOLD"; - Colour = PIXPACK(0xDCAD2C); + Colour = 0xDCAD2C_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -55,7 +55,7 @@ static int update(UPDATE_FUNC_ARGS) static int checkCoordsY[] = { 0, 0, -4, 4 }; //Find nearby rusted iron (BMTL with tmp 1+) for(int j = 0; j < 8; j++){ - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); rx = (rndstore % 9)-4; rndstore >>= 4; ry = (rndstore % 9)-4; @@ -89,7 +89,7 @@ static int update(UPDATE_FUNC_ARGS) } if (TYP(sim->photons[y][x]) == PT_NEUT) { - if (RNG::Ref().chance(1, 7)) + if (sim->rng.chance(1, 7)) { sim->kill_part(ID(sim->photons[y][x])); } @@ -99,7 +99,7 @@ static int update(UPDATE_FUNC_ARGS) static int graphics(GRAPHICS_FUNC_ARGS) { - int rndstore = RNG::Ref().gen(); + int rndstore = ren->rng.gen(); *colr += (rndstore % 10) - 5; rndstore >>= 4; *colg += (rndstore % 10)- 5; diff --git a/src/simulation/elements/GOO.cpp b/src/simulation/elements/GOO.cpp index 8f109713b..1db9df5d7 100644 --- a/src/simulation/elements/GOO.cpp +++ b/src/simulation/elements/GOO.cpp @@ -6,7 +6,7 @@ void Element::Element_GOO() { Identifier = "DEFAULT_PT_GOO"; Name = "GOO"; - Colour = PIXPACK(0x804000); + Colour = 0x804000_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -50,7 +50,7 @@ constexpr float ADVECTION = 0.1f; static int update(UPDATE_FUNC_ARGS) { if (!parts[i].life && sim->pv[y/CELL][x/CELL]>1.0f) - parts[i].life = RNG::Ref().between(300, 379); + parts[i].life = sim->rng.between(300, 379); if (parts[i].life) { parts[i].vx += ADVECTION*sim->vx[y/CELL][x/CELL]; diff --git a/src/simulation/elements/GPMP.cpp b/src/simulation/elements/GPMP.cpp index aaed7af39..d9386749b 100644 --- a/src/simulation/elements/GPMP.cpp +++ b/src/simulation/elements/GPMP.cpp @@ -7,7 +7,7 @@ void Element::Element_GPMP() { Identifier = "DEFAULT_PT_GPMP"; Name = "GPMP"; - Colour = PIXPACK(0x0A3B3B); + Colour = 0x0A3B3B_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; @@ -64,7 +64,7 @@ static int update(UPDATE_FUNC_ARGS) if (parts[i].temp<= -256.0f+273.15f) parts[i].temp = -256.0f+273.15f; - sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = 0.2f*(parts[i].temp-273.15); + sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] = 0.2f*(parts[i].temp-273.15); for (rx=-2; rx<3; rx++) for (ry=-2; ry<3; ry++) if (BOUNDS_CHECK && (rx || ry)) diff --git a/src/simulation/elements/GRAV.cpp b/src/simulation/elements/GRAV.cpp index 4fca0d105..08e1ee79c 100644 --- a/src/simulation/elements/GRAV.cpp +++ b/src/simulation/elements/GRAV.cpp @@ -8,7 +8,7 @@ void Element::Element_GRAV() { Identifier = "DEFAULT_PT_GRAV"; Name = "GRAV"; - Colour = PIXPACK(0x202020); + Colour = 0x202020_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -50,7 +50,7 @@ void Element::Element_GRAV() static int update(UPDATE_FUNC_ARGS) { - if (parts[i].vx*parts[i].vx + parts[i].vy*parts[i].vy >= 0.1f && RNG::Ref().chance(1, 512)) + if (parts[i].vx*parts[i].vx + parts[i].vy*parts[i].vy >= 0.1f && sim->rng.chance(1, 512)) { if (!parts[i].life) parts[i].life = 48; diff --git a/src/simulation/elements/GRVT.cpp b/src/simulation/elements/GRVT.cpp index c12e90898..55fc9608a 100644 --- a/src/simulation/elements/GRVT.cpp +++ b/src/simulation/elements/GRVT.cpp @@ -8,7 +8,7 @@ void Element::Element_GRVT() { Identifier = "DEFAULT_PT_GRVT"; Name = "GRVT"; - Colour = PIXPACK(0x00EE76); + Colour = 0x00EE76_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -59,7 +59,7 @@ static int update(UPDATE_FUNC_ARGS) if (parts[i].tmp <= -100) parts[i].tmp = -100; - sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] = 0.2f*parts[i].tmp; + sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] = 0.2f*parts[i].tmp; return 0; } @@ -76,8 +76,8 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - float a = RNG::Ref().between(0, 359) * 3.14159f / 180.0f; - sim->parts[i].life = 250 + RNG::Ref().between(0, 199); + float a = sim->rng.between(0, 359) * 3.14159f / 180.0f; + sim->parts[i].life = 250 + sim->rng.between(0, 199); sim->parts[i].vx = 2.0f*cosf(a); sim->parts[i].vy = 2.0f*sinf(a); } diff --git a/src/simulation/elements/GUNP.cpp b/src/simulation/elements/GUNP.cpp index 63cbe96ce..8f351b485 100644 --- a/src/simulation/elements/GUNP.cpp +++ b/src/simulation/elements/GUNP.cpp @@ -4,7 +4,7 @@ void Element::Element_GUNP() { Identifier = "DEFAULT_PT_GUNP"; Name = "GUN"; - Colour = PIXPACK(0xC0C0D0); + Colour = 0xC0C0D0_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; diff --git a/src/simulation/elements/H2.cpp b/src/simulation/elements/H2.cpp index 35004ae59..2b959c5f3 100644 --- a/src/simulation/elements/H2.cpp +++ b/src/simulation/elements/H2.cpp @@ -6,7 +6,7 @@ void Element::Element_H2() { Identifier = "DEFAULT_PT_H2"; Name = "HYGN"; - Colour = PIXPACK(0x5070FF); + Colour = 0x5070FF_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -77,14 +77,14 @@ static int update(UPDATE_FUNC_ARGS) parts[ID(r)].temp=2473.15f; parts[ID(r)].tmp |= 1; sim->create_part(i,x,y,PT_FIRE); - parts[i].temp += RNG::Ref().between(0, 99); + parts[i].temp += sim->rng.between(0, 99); parts[i].tmp |= 1; return 1; } else if ((rt==PT_PLSM && !(parts[ID(r)].tmp&4)) || (rt==PT_LAVA && parts[ID(r)].ctype != PT_BMTL)) { sim->create_part(i,x,y,PT_FIRE); - parts[i].temp += RNG::Ref().between(0, 99); + parts[i].temp += sim->rng.between(0, 99); parts[i].tmp |= 1; return 1; } @@ -92,7 +92,7 @@ static int update(UPDATE_FUNC_ARGS) } if (parts[i].temp > 2273.15 && sim->pv[y/CELL][x/CELL] > 50.0f) { - if (RNG::Ref().chance(1, 5)) + if (sim->rng.chance(1, 5)) { int j; float temp = parts[i].temp; @@ -102,7 +102,7 @@ static int update(UPDATE_FUNC_ARGS) j = sim->create_part(-3,x,y,PT_NEUT); if (j>-1) parts[j].temp = temp; - if (RNG::Ref().chance(1, 10)) + if (sim->rng.chance(1, 10)) { j = sim->create_part(-3,x,y,PT_ELEC); if (j>-1) @@ -115,7 +115,7 @@ static int update(UPDATE_FUNC_ARGS) parts[j].temp = temp; parts[j].tmp = 0x1; } - rx = x + RNG::Ref().between(-1, 1), ry = y + RNG::Ref().between(-1, 1), rt = TYP(pmap[ry][rx]); + rx = x + sim->rng.between(-1, 1), ry = y + sim->rng.between(-1, 1), rt = TYP(pmap[ry][rx]); if (sim->can_move[PT_PLSM][rt] || rt == PT_H2) { j = sim->create_part(-3,rx,ry,PT_PLSM); @@ -125,7 +125,7 @@ static int update(UPDATE_FUNC_ARGS) parts[j].tmp |= 4; } } - parts[i].temp = temp + RNG::Ref().between(750, 1249); + parts[i].temp = temp + sim->rng.between(750, 1249); sim->pv[y/CELL][x/CELL] += 30; return 1; } diff --git a/src/simulation/elements/HEAC.cpp b/src/simulation/elements/HEAC.cpp index 0a11cee77..9fdb10928 100644 --- a/src/simulation/elements/HEAC.cpp +++ b/src/simulation/elements/HEAC.cpp @@ -7,7 +7,7 @@ void Element::Element_HEAC() { Identifier = "DEFAULT_PT_HEAC"; Name = "HEAC"; - Colour = PIXPACK(0xCB6351); + Colour = 0xCB6351_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/HSWC.cpp b/src/simulation/elements/HSWC.cpp index f34198860..8e6fba41f 100644 --- a/src/simulation/elements/HSWC.cpp +++ b/src/simulation/elements/HSWC.cpp @@ -7,7 +7,7 @@ void Element::Element_HSWC() { Identifier = "DEFAULT_PT_HSWC"; Name = "HSWC"; - Colour = PIXPACK(0x3B0A0A); + Colour = 0x3B0A0A_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; diff --git a/src/simulation/elements/ICEI.cpp b/src/simulation/elements/ICEI.cpp index 5445783f8..8621adf38 100644 --- a/src/simulation/elements/ICEI.cpp +++ b/src/simulation/elements/ICEI.cpp @@ -6,7 +6,7 @@ void Element::Element_ICEI() { Identifier = "DEFAULT_PT_ICEI"; Name = "ICE"; - Colour = PIXPACK(0xA0C0FF); + Colour = 0xA0C0FF_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -64,14 +64,14 @@ static int update(UPDATE_FUNC_ARGS) continue; if (TYP(r)==PT_SALT || TYP(r)==PT_SLTW) { - if (parts[i].temp > sim->elements[PT_SLTW].LowTemperature && RNG::Ref().chance(1, 200)) + if (parts[i].temp > sim->elements[PT_SLTW].LowTemperature && sim->rng.chance(1, 200)) { sim->part_change_type(i,x,y,PT_SLTW); sim->part_change_type(ID(r),x+rx,y+ry,PT_SLTW); return 0; } } - else if ((TYP(r)==PT_FRZZ) && RNG::Ref().chance(1, 200)) + else if ((TYP(r)==PT_FRZZ) && sim->rng.chance(1, 200)) { sim->part_change_type(ID(r),x+rx,y+ry,PT_ICEI); parts[ID(r)].ctype = PT_FRZW; diff --git a/src/simulation/elements/IGNT.cpp b/src/simulation/elements/IGNT.cpp index 624a68b3d..b52369e2b 100644 --- a/src/simulation/elements/IGNT.cpp +++ b/src/simulation/elements/IGNT.cpp @@ -6,7 +6,7 @@ void Element::Element_IGNT() { Identifier = "DEFAULT_PT_IGNT"; Name = "IGNC"; - Colour = PIXPACK(0xC0B050); + Colour = 0xC0B050_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -68,20 +68,20 @@ static int update(UPDATE_FUNC_ARGS) } else if(parts[i].life > 0) { - if (RNG::Ref().chance(2, 3)) + if (sim->rng.chance(2, 3)) { - int nb = sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), PT_EMBR); + int nb = sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), PT_EMBR); if (nb!=-1) { parts[nb].tmp = 0; parts[nb].life = 30; - parts[nb].vx = float(RNG::Ref().between(-10, 10)); - parts[nb].vy = float(RNG::Ref().between(-10, 10)); + parts[nb].vx = float(sim->rng.between(-10, 10)); + parts[nb].vy = float(sim->rng.between(-10, 10)); parts[nb].temp = restrict_flt(parts[i].temp-273.15f+400.0f, MIN_TEMP, MAX_TEMP); } } else { - sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), PT_FIRE); + sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), PT_FIRE); } parts[i].life--; } diff --git a/src/simulation/elements/INSL.cpp b/src/simulation/elements/INSL.cpp index 980edbed2..65a48f12c 100644 --- a/src/simulation/elements/INSL.cpp +++ b/src/simulation/elements/INSL.cpp @@ -4,7 +4,7 @@ void Element::Element_INSL() { Identifier = "DEFAULT_PT_INSL"; Name = "INSL"; - Colour = PIXPACK(0x9EA3B6); + Colour = 0x9EA3B6_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/INST.cpp b/src/simulation/elements/INST.cpp index 3e50ceeb1..d93e05973 100644 --- a/src/simulation/elements/INST.cpp +++ b/src/simulation/elements/INST.cpp @@ -4,7 +4,7 @@ void Element::Element_INST() { Identifier = "DEFAULT_PT_INST"; Name = "INST"; - Colour = PIXPACK(0x404039); + Colour = 0x404039_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/INVIS.cpp b/src/simulation/elements/INVIS.cpp index d03373de1..8966ab860 100644 --- a/src/simulation/elements/INVIS.cpp +++ b/src/simulation/elements/INVIS.cpp @@ -7,7 +7,7 @@ void Element::Element_INVIS() { Identifier = "DEFAULT_PT_INVIS"; Name = "INVS"; - Colour = PIXPACK(0x00CCCC); + Colour = 0x00CCCC_rgb; MenuVisible = 1; MenuSection = SC_SENSOR; Enabled = 1; diff --git a/src/simulation/elements/INWR.cpp b/src/simulation/elements/INWR.cpp index 9d1137ec8..9c830fa44 100644 --- a/src/simulation/elements/INWR.cpp +++ b/src/simulation/elements/INWR.cpp @@ -4,7 +4,7 @@ void Element::Element_INWR() { Identifier = "DEFAULT_PT_INWR"; Name = "INWR"; - Colour = PIXPACK(0x544141); + Colour = 0x544141_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/IRON.cpp b/src/simulation/elements/IRON.cpp index 45ef6c76d..b68a71618 100644 --- a/src/simulation/elements/IRON.cpp +++ b/src/simulation/elements/IRON.cpp @@ -6,7 +6,7 @@ void Element::Element_IRON() { Identifier = "DEFAULT_PT_IRON"; Name = "IRON"; - Colour = PIXPACK(0x707070); + Colour = 0x707070_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -55,22 +55,22 @@ static int update(UPDATE_FUNC_ARGS) if (BOUNDS_CHECK && (rx || ry)) { r = pmap[y+ry][x+rx]; - switch TYP(r) + switch (TYP(r)) { case PT_SALT: - if (RNG::Ref().chance(1, 47)) + if (sim->rng.chance(1, 47)) goto succ; break; case PT_SLTW: - if (RNG::Ref().chance(1, 67)) + if (sim->rng.chance(1, 67)) goto succ; break; case PT_WATR: - if (RNG::Ref().chance(1, 1200)) + if (sim->rng.chance(1, 1200)) goto succ; break; case PT_O2: - if (RNG::Ref().chance(1, 250)) + if (sim->rng.chance(1, 250)) goto succ; break; case PT_LO2: @@ -82,6 +82,6 @@ static int update(UPDATE_FUNC_ARGS) return 0; succ: sim->part_change_type(i,x,y,PT_BMTL); - parts[i].tmp = RNG::Ref().between(20, 29); + parts[i].tmp = sim->rng.between(20, 29); return 0; } diff --git a/src/simulation/elements/ISOZ.cpp b/src/simulation/elements/ISOZ.cpp index 20480cd0a..62c0cfc45 100644 --- a/src/simulation/elements/ISOZ.cpp +++ b/src/simulation/elements/ISOZ.cpp @@ -6,7 +6,7 @@ void Element::Element_ISOZ() { Identifier = "DEFAULT_PT_ISOZ"; Name = "ISOZ"; - Colour = PIXPACK(0xAA30D0); + Colour = 0xAA30D0_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -49,11 +49,11 @@ void Element::Element_ISOZ() static int update(UPDATE_FUNC_ARGS) { float rr, rrr; - if (RNG::Ref().chance(1, 200) && RNG::Ref().chance(int(-4.0f * sim->pv[y/CELL][x/CELL]), 1000)) + if (sim->rng.chance(1, 200) && sim->rng.chance(int(-4.0f * sim->pv[y/CELL][x/CELL]), 1000)) { sim->create_part(i, x, y, PT_PHOT); - rr = RNG::Ref().between(128, 355) / 127.0f; - rrr = RNG::Ref().between(0, 359) * 3.14159f / 180.0f; + rr = sim->rng.between(128, 355) / 127.0f; + rrr = sim->rng.between(0, 359) * 3.14159f / 180.0f; parts[i].vx = rr*cosf(rrr); parts[i].vy = rr*sinf(rrr); } diff --git a/src/simulation/elements/ISZS.cpp b/src/simulation/elements/ISZS.cpp index 2864f5268..fc9146b18 100644 --- a/src/simulation/elements/ISZS.cpp +++ b/src/simulation/elements/ISZS.cpp @@ -6,7 +6,7 @@ void Element::Element_ISZS() { Identifier = "DEFAULT_PT_ISZS"; Name = "ISZS"; - Colour = PIXPACK(0x662089); + Colour = 0x662089_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -49,11 +49,11 @@ void Element::Element_ISZS() static int update(UPDATE_FUNC_ARGS) { float rr, rrr; - if (RNG::Ref().chance(1, 200) && RNG::Ref().chance(int(-4.0f * sim->pv[y/CELL][x/CELL]), 1000)) + if (sim->rng.chance(1, 200) && sim->rng.chance(int(-4.0f * sim->pv[y/CELL][x/CELL]), 1000)) { sim->create_part(i, x, y, PT_PHOT); - rr = RNG::Ref().between(128, 355) / 127.0f; - rrr = RNG::Ref().between(0, 359) * 3.14159f / 180.0f; + rr = sim->rng.between(128, 355) / 127.0f; + rrr = sim->rng.between(0, 359) * 3.14159f / 180.0f; parts[i].vx = rr*cosf(rrr); parts[i].vy = rr*sinf(rrr); } diff --git a/src/simulation/elements/LAVA.cpp b/src/simulation/elements/LAVA.cpp index 0e998017b..02c8583d3 100644 --- a/src/simulation/elements/LAVA.cpp +++ b/src/simulation/elements/LAVA.cpp @@ -8,7 +8,7 @@ void Element::Element_LAVA() { Identifier = "DEFAULT_PT_LAVA"; Name = "LAVA"; - Colour = PIXPACK(0xE05010); + Colour = 0xE05010_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -35,6 +35,7 @@ void Element::Element_LAVA() Description = "Molten lava. Ignites flammable materials. Generated when metals and other materials melt, solidifies when cold."; Properties = TYPE_LIQUID|PROP_LIFE_DEC; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -70,5 +71,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].life = RNG::Ref().between(240, 359); + sim->parts[i].life = sim->rng.between(240, 359); } diff --git a/src/simulation/elements/LCRY.cpp b/src/simulation/elements/LCRY.cpp index 2c1891009..fa5708290 100644 --- a/src/simulation/elements/LCRY.cpp +++ b/src/simulation/elements/LCRY.cpp @@ -7,7 +7,7 @@ void Element::Element_LCRY() { Identifier = "DEFAULT_PT_LCRY"; Name = "LCRY"; - Colour = PIXPACK(0x505050); + Colour = 0x505050_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; diff --git a/src/simulation/elements/LDTC.cpp b/src/simulation/elements/LDTC.cpp index c3ba30e87..f4ad432ee 100644 --- a/src/simulation/elements/LDTC.cpp +++ b/src/simulation/elements/LDTC.cpp @@ -7,7 +7,7 @@ void Element::Element_LDTC() { Identifier = "DEFAULT_PT_LDTC"; Name = "LDTC"; - Colour = PIXPACK(0x66ff66); + Colour = 0x66ff66_rgb; MenuVisible = 1; MenuSection = SC_SENSOR; Enabled = 1; @@ -33,6 +33,7 @@ void Element::Element_LDTC() Description = "Linear detector. Scans in 8 directions for particles with its ctype and creates a spark on the opposite side."; Properties = TYPE_SOLID | PROP_NOCTYPEDRAW; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/LIFE.cpp b/src/simulation/elements/LIFE.cpp index 2e28ee262..184df2f44 100644 --- a/src/simulation/elements/LIFE.cpp +++ b/src/simulation/elements/LIFE.cpp @@ -7,7 +7,7 @@ void Element::Element_LIFE() { Identifier = "DEFAULT_PT_LIFE"; Name = "LIFE"; - Colour = PIXPACK(0x0CAC00); + Colour = 0x0CAC00_rgb; MenuVisible = 0; MenuSection = SC_LIFE; Enabled = 1; @@ -50,11 +50,11 @@ void Element::Element_LIFE() static int graphics(GRAPHICS_FUNC_ARGS) { - auto colour1 = cpart->dcolour; - auto colour2 = cpart->tmp; - if (!colour1) + auto colour1 = RGB::Unpack(cpart->dcolour); + auto colour2 = RGB::Unpack(cpart->tmp); + if (!cpart->dcolour) { - colour1 = PIXPACK(0xFFFFFF); + colour1 = 0xFFFFFF_rgb; } auto ruleset = cpart->ctype; bool renderDeco = !ren->blackDecorations; @@ -73,16 +73,16 @@ static int graphics(GRAPHICS_FUNC_ARGS) auto states = ((ruleset >> 17) & 0xF) + 2; if (states == 2) { - *colr = PIXR(colour1); - *colg = PIXG(colour1); - *colb = PIXB(colour1); + *colr = colour1.Red; + *colg = colour1.Green; + *colb = colour1.Blue; } else { auto mul = (cpart->tmp2 - 1) / float(states - 2); - *colr = int(PIXR(colour1) * mul + PIXR(colour2) * (1.f - mul)); - *colg = int(PIXG(colour1) * mul + PIXG(colour2) * (1.f - mul)); - *colb = int(PIXB(colour1) * mul + PIXB(colour2) * (1.f - mul)); + *colr = int(colour1.Red * mul + colour2.Red * (1.f - mul)); + *colg = int(colour1.Green * mul + colour2.Green * (1.f - mul)); + *colb = int(colour1.Blue * mul + colour2.Blue * (1.f - mul)); } } *pixel_mode |= NO_DECO; @@ -99,8 +99,8 @@ static void create(ELEMENT_CREATE_FUNC_ARGS) sim->parts[i].ctype = v; if (v < NGOL) { - sim->parts[i].dcolour = builtinGol[v].colour; - sim->parts[i].tmp = builtinGol[v].colour2; + sim->parts[i].dcolour = builtinGol[v].colour.Pack(); + sim->parts[i].tmp = builtinGol[v].colour2.Pack(); v = builtinGol[v].ruleset; } else if (!skipLookup) diff --git a/src/simulation/elements/LIGH.cpp b/src/simulation/elements/LIGH.cpp index abe9f3d56..4869cc4b6 100644 --- a/src/simulation/elements/LIGH.cpp +++ b/src/simulation/elements/LIGH.cpp @@ -9,7 +9,7 @@ void Element::Element_LIGH() { Identifier = "DEFAULT_PT_LIGH"; Name = "LIGH"; - Colour = PIXPACK(0xFFFFC0); + Colour = 0xFFFFC0_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -91,11 +91,11 @@ static int update(UPDATE_FUNC_ARGS) rt = TYP(r); if ((surround_space || sim->elements[rt].Explosive) && (rt!=PT_SPNG || parts[ID(r)].life==0) && - sim->elements[rt].Flammable && RNG::Ref().chance(sim->elements[rt].Flammable + int(sim->pv[(y+ry)/CELL][(x+rx)/CELL] * 10.0f), 1000)) + sim->elements[rt].Flammable && sim->rng.chance(sim->elements[rt].Flammable + int(sim->pv[(y+ry)/CELL][(x+rx)/CELL] * 10.0f), 1000)) { sim->part_change_type(ID(r),x+rx,y+ry,PT_FIRE); parts[ID(r)].temp = restrict_flt(sim->elements[PT_FIRE].DefaultProperties.temp + (sim->elements[rt].Flammable/2), MIN_TEMP, MAX_TEMP); - parts[ID(r)].life = RNG::Ref().between(180, 259); + parts[ID(r)].life = sim->rng.between(180, 259); parts[ID(r)].tmp = parts[ID(r)].ctype = 0; if (sim->elements[rt].Explosive) sim->pv[y/CELL][x/CELL] += 0.25f * CFDS; @@ -115,12 +115,12 @@ static int update(UPDATE_FUNC_ARGS) case PT_PLUT: parts[ID(r)].temp = restrict_flt(parts[ID(r)].temp+powderful, MIN_TEMP, MAX_TEMP); sim->pv[y/CELL][x/CELL] +=powderful/35; - if (RNG::Ref().chance(1, 3)) + if (sim->rng.chance(1, 3)) { sim->part_change_type(ID(r),x+rx,y+ry,PT_NEUT); - parts[ID(r)].life = RNG::Ref().between(480, 959); - parts[ID(r)].vx = float(RNG::Ref().between(-5, 5)); - parts[ID(r)].vy = float(RNG::Ref().between(-5, 5)); + parts[ID(r)].life = sim->rng.between(480, 959); + parts[ID(r)].vx = float(sim->rng.between(-5, 5)); + parts[ID(r)].vy = float(sim->rng.between(-5, 5)); } break; case PT_COAL: @@ -167,16 +167,16 @@ static int update(UPDATE_FUNC_ARGS) sim->kill_part(i); return 1; } - angle = float((parts[i].tmp + RNG::Ref().between(-30, 30)) % 360); - multipler = int(parts[i].life * 1.5) + RNG::Ref().between(0, parts[i].life); - rx=int(cos(angle*M_PI/180)*multipler); - ry=int(-sin(angle*M_PI/180)*multipler); + angle = float((parts[i].tmp + sim->rng.between(-30, 30)) % 360); + multipler = int(parts[i].life * 1.5) + sim->rng.between(0, parts[i].life); + rx=int(cos(angle*TPT_PI_FLT/180)*multipler); + ry=int(-sin(angle*TPT_PI_FLT/180)*multipler); create_line_par(sim, x, y, x+rx, y+ry, PT_LIGH, parts[i].temp, parts[i].life, int(angle), parts[i].tmp2, i); if (parts[i].tmp2 == 2)// && pNear == -1) { - angle2 = float(((int)angle + RNG::Ref().between(-100, 100)) % 360); - rx=int(cos(angle2*M_PI/180)*multipler); - ry=int(-sin(angle2*M_PI/180)*multipler); + angle2 = float(((int)angle + sim->rng.between(-100, 100)) % 360); + rx=int(cos(angle2*TPT_PI_FLT/180)*multipler); + ry=int(-sin(angle2*TPT_PI_FLT/180)*multipler); create_line_par(sim, x, y, x+rx, y+ry, PT_LIGH, parts[i].temp, parts[i].life, int(angle2), parts[i].tmp2, i); } @@ -193,12 +193,12 @@ static bool create_LIGH(Simulation * sim, int x, int y, int c, float temp, int l sim->parts[p].tmp = tmp; if (last) { - int nextSegmentLife = (int)(life/1.5 - RNG::Ref().between(0, 1)); + int nextSegmentLife = (int)(life/1.5 - sim->rng.between(0, 1)); sim->parts[p].life = nextSegmentLife; if (nextSegmentLife > 1) { // Decide whether to branch or to bend - bool doBranch = RNG::Ref().chance(7, 10); + bool doBranch = sim->rng.chance(7, 10); sim->parts[p].tmp2 = (doBranch ? 2 : 0) + (p > i && tmp2 != 4 ? 1 : 0); } // Not enough energy to continue @@ -315,12 +315,12 @@ static void create(ELEMENT_CREATE_FUNC_ARGS) gsize = gx * gx + gy * gy; if (gsize < 0.0016f) { - float angle = RNG::Ref().between(0, 6283) * 0.001f; //(in radians, between 0 and 2*pi) + float angle = sim->rng.between(0, 6283) * 0.001f; //(in radians, between 0 and 2*pi) gsize = sqrtf(gsize); // randomness in weak gravity fields (more randomness with weaker fields) gx += cosf(angle) * (0.04f - gsize); gy += sinf(angle) * (0.04f - gsize); } - sim->parts[i].tmp = (static_cast(atan2f(-gy, gx) * (180.0f / M_PI)) + RNG::Ref().between(-20, 20) + 360) % 360; + sim->parts[i].tmp = (static_cast(atan2f(-gy, gx) * (180.0f / TPT_PI_FLT)) + sim->rng.between(-20, 20) + 360) % 360; sim->parts[i].tmp2 = 4; } diff --git a/src/simulation/elements/LITH.cpp b/src/simulation/elements/LITH.cpp index ad804c388..e25ce10aa 100644 --- a/src/simulation/elements/LITH.cpp +++ b/src/simulation/elements/LITH.cpp @@ -7,7 +7,7 @@ void Element::Element_LITH() { Identifier = "DEFAULT_PT_LITH"; Name = "LITH"; - Colour = PIXPACK(0xB6AABF); + Colour = 0xB6AABF_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -85,7 +85,7 @@ static int update(UPDATE_FUNC_ARGS) int neighborData = pmap[y + ry][x + rx]; if (!neighborData) { - if (burnTimer > 1012 && RNG::Ref().chance(1, 10)) + if (burnTimer > 1012 && sim->rng.chance(1, 10)) { sim->create_part(-1, x + rx, y + ry, PT_FIRE); } @@ -163,7 +163,7 @@ static int update(UPDATE_FUNC_ARGS) break; case PT_FIRE: - if (self.temp > 440.f && RNG::Ref().chance(1, 40) && hydrogenationFactor < 6) + if (self.temp > 440.f && sim->rng.chance(1, 40) && hydrogenationFactor < 6) { burnTimer = 1013; hydrogenationFactor += 1; @@ -171,7 +171,7 @@ static int update(UPDATE_FUNC_ARGS) break; case PT_O2: - if (burnTimer > 1000 && RNG::Ref().chance(1, 10)) + if (burnTimer > 1000 && sim->rng.chance(1, 10)) { sim->part_change_type(i, x, y, PT_PLSM); sim->part_change_type(ID(neighborData), x + rx, y + ry, PT_PLSM); @@ -196,8 +196,8 @@ static int update(UPDATE_FUNC_ARGS) for (int trade = 0; trade < 9; ++trade) { - int rx = RNG::Ref().between(-3, 3); - int ry = RNG::Ref().between(-3, 3); + int rx = sim->rng.between(-3, 3); + int ry = sim->rng.between(-3, 3); if (BOUNDS_CHECK && (rx || ry)) { int neighborData = pmap[y + ry][x + rx]; @@ -248,16 +248,16 @@ static int graphics(GRAPHICS_FUNC_ARGS) // Exploding lith if (cpart->life >= 1000) { - int colour = 0xFFA040; - *colr = PIXR(colour); - *colg = PIXG(colour); - *colb = PIXB(colour); + auto colour = 0xFFA040_rgb; + *colr = colour.Red; + *colg = colour.Green; + *colb = colour.Blue; *pixel_mode |= PMODE_FLARE | PMODE_GLOW; } // Charged lith else if (cpart->ctype > 0) { - int mult = RNG::Ref().between(cpart->ctype / 3, cpart->ctype) / 15; + int mult = ren->rng.between(cpart->ctype / 3, cpart->ctype) / 15; mult = std::min(6, mult); *colr -= 30 * mult; *colb += 20 * mult; diff --git a/src/simulation/elements/LNTG.cpp b/src/simulation/elements/LNTG.cpp index ac566e3a3..3b43a7878 100644 --- a/src/simulation/elements/LNTG.cpp +++ b/src/simulation/elements/LNTG.cpp @@ -4,7 +4,7 @@ void Element::Element_LNTG() { Identifier = "DEFAULT_PT_LNTG"; Name = "LN2"; - Colour = PIXPACK(0x80A0DF); + Colour = 0x80A0DF_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/LO2.cpp b/src/simulation/elements/LO2.cpp index 9efc2acb7..430512cb8 100644 --- a/src/simulation/elements/LO2.cpp +++ b/src/simulation/elements/LO2.cpp @@ -4,7 +4,7 @@ void Element::Element_LO2() { Identifier = "DEFAULT_PT_LO2"; Name = "LOXY"; - Colour = PIXPACK(0x80A0EF); + Colour = 0x80A0EF_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/LOLZ.cpp b/src/simulation/elements/LOLZ.cpp index abd9d598e..aa9ee84ab 100644 --- a/src/simulation/elements/LOLZ.cpp +++ b/src/simulation/elements/LOLZ.cpp @@ -4,7 +4,7 @@ void Element::Element_LOLZ() { Identifier = "DEFAULT_PT_LOLZ"; Name = "LOLZ"; - Colour = PIXPACK(0x569212); + Colour = 0x569212_rgb; MenuVisible = 1; MenuSection = SC_CRACKER2; Enabled = 1; diff --git a/src/simulation/elements/LOVE.cpp b/src/simulation/elements/LOVE.cpp index 9e1c6c040..f679b7999 100644 --- a/src/simulation/elements/LOVE.cpp +++ b/src/simulation/elements/LOVE.cpp @@ -4,7 +4,7 @@ void Element::Element_LOVE() { Identifier = "DEFAULT_PT_LOVE"; Name = "LOVE"; - Colour = PIXPACK(0xFF30FF); + Colour = 0xFF30FF_rgb; MenuVisible = 1; MenuSection = SC_CRACKER2; Enabled = 1; diff --git a/src/simulation/elements/LRBD.cpp b/src/simulation/elements/LRBD.cpp index b7716ac86..b1b46d502 100644 --- a/src/simulation/elements/LRBD.cpp +++ b/src/simulation/elements/LRBD.cpp @@ -4,7 +4,7 @@ void Element::Element_LRBD() { Identifier = "DEFAULT_PT_LRBD"; Name = "LRBD"; - Colour = PIXPACK(0xAAAAAA); + Colour = 0xAAAAAA_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; diff --git a/src/simulation/elements/LSNS.cpp b/src/simulation/elements/LSNS.cpp index f65a956d2..6521ba8f4 100644 --- a/src/simulation/elements/LSNS.cpp +++ b/src/simulation/elements/LSNS.cpp @@ -6,7 +6,7 @@ void Element::Element_LSNS() { Identifier = "DEFAULT_PT_LSNS"; Name = "LSNS"; - Colour = PIXPACK(0x336699); + Colour = 0x336699_rgb; MenuVisible = 1; MenuSection = SC_SENSOR; Enabled = 1; diff --git a/src/simulation/elements/MERC.cpp b/src/simulation/elements/MERC.cpp index a546b2d89..b7d5e79a8 100644 --- a/src/simulation/elements/MERC.cpp +++ b/src/simulation/elements/MERC.cpp @@ -6,7 +6,7 @@ void Element::Element_MERC() { Identifier = "DEFAULT_PT_MERC"; Name = "MERC"; - Colour = PIXPACK(0x736B6D); + Colour = 0x736B6D_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -56,7 +56,7 @@ static int update(UPDATE_FUNC_ARGS) if (parts[i].temp + 1 == 0) parts[i].temp = 0; int maxtmp = int(absorbScale/(parts[i].temp + 1))-1; - if (RNG::Ref().chance(absorbScale%(int(parts[i].temp)+1), int(parts[i].temp)+1)) + if (sim->rng.chance(absorbScale%(int(parts[i].temp)+1), int(parts[i].temp)+1)) maxtmp ++; if (parts[i].tmp < 0) @@ -77,7 +77,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r || (parts[i].tmp >=maxtmp)) continue; - if (TYP(r)==PT_MERC&& RNG::Ref().chance(1, 3)) + if (TYP(r)==PT_MERC&& sim->rng.chance(1, 3)) { if ((parts[i].tmp + parts[ID(r)].tmp + 1) <= maxtmp) { @@ -107,8 +107,8 @@ static int update(UPDATE_FUNC_ARGS) } for ( trade = 0; trade<4; trade ++) { - rx = RNG::Ref().between(-2, 2); - ry = RNG::Ref().between(-2, 2); + rx = sim->rng.between(-2, 2); + ry = sim->rng.between(-2, 2); if (BOUNDS_CHECK && (rx || ry)) { r = pmap[y+ry][x+rx]; diff --git a/src/simulation/elements/METL.cpp b/src/simulation/elements/METL.cpp index 44ff6a98a..bcb3656ef 100644 --- a/src/simulation/elements/METL.cpp +++ b/src/simulation/elements/METL.cpp @@ -4,7 +4,7 @@ void Element::Element_METL() { Identifier = "DEFAULT_PT_METL"; Name = "METL"; - Colour = PIXPACK(0x404060); + Colour = 0x404060_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/MORT.cpp b/src/simulation/elements/MORT.cpp index 925056ad6..b2ee36d3c 100644 --- a/src/simulation/elements/MORT.cpp +++ b/src/simulation/elements/MORT.cpp @@ -6,7 +6,7 @@ void Element::Element_MORT() { Identifier = "DEFAULT_PT_MORT"; Name = "MORT"; - Colour = PIXPACK(0xE0E0E0); + Colour = 0xE0E0E0_rgb; MenuVisible = 1; MenuSection = SC_CRACKER2; Enabled = 1; diff --git a/src/simulation/elements/MWAX.cpp b/src/simulation/elements/MWAX.cpp index 64d0d3e26..2a2c024fe 100644 --- a/src/simulation/elements/MWAX.cpp +++ b/src/simulation/elements/MWAX.cpp @@ -4,7 +4,7 @@ void Element::Element_MWAX() { Identifier = "DEFAULT_PT_MWAX"; Name = "MWAX"; - Colour = PIXPACK(0xE0E0AA); + Colour = 0xE0E0AA_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/NBHL.cpp b/src/simulation/elements/NBHL.cpp index f0f7fe45d..1aa5a894b 100644 --- a/src/simulation/elements/NBHL.cpp +++ b/src/simulation/elements/NBHL.cpp @@ -6,7 +6,7 @@ void Element::Element_NBHL() { Identifier = "DEFAULT_PT_NBHL"; Name = "BHOL"; - Colour = PIXPACK(0x202020); + Colour = 0x202020_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -48,8 +48,8 @@ void Element::Element_NBHL() static int update(UPDATE_FUNC_ARGS) { if (parts[i].tmp) - sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] += restrict_flt(0.001f*parts[i].tmp, 0.1f, 51.2f); + sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] += restrict_flt(0.001f*parts[i].tmp, 0.1f, 51.2f); else - sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] += 0.1f; + sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] += 0.1f; return 0; } diff --git a/src/simulation/elements/NBLE.cpp b/src/simulation/elements/NBLE.cpp index 20176a08d..b1ee12974 100644 --- a/src/simulation/elements/NBLE.cpp +++ b/src/simulation/elements/NBLE.cpp @@ -6,7 +6,7 @@ void Element::Element_NBLE() { Identifier = "DEFAULT_PT_NBLE"; Name = "NBLE"; - Colour = PIXPACK(0xEB4917); + Colour = 0xEB4917_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -51,7 +51,7 @@ static int update(UPDATE_FUNC_ARGS) if (parts[i].temp > 5273.15 && sim->pv[y/CELL][x/CELL] > 100.0f) { parts[i].tmp |= 0x1; - if (RNG::Ref().chance(1, 5)) + if (sim->rng.chance(1, 5)) { int j; float temp = parts[i].temp; @@ -60,7 +60,7 @@ static int update(UPDATE_FUNC_ARGS) j = sim->create_part(-3,x,y,PT_NEUT); if (j != -1) parts[j].temp = temp; - if (RNG::Ref().chance(1, 25)) + if (sim->rng.chance(1, 25)) { j = sim->create_part(-3,x,y,PT_ELEC); if (j != -1) @@ -73,7 +73,7 @@ static int update(UPDATE_FUNC_ARGS) parts[j].temp = temp; parts[j].tmp = 0x1; } - int rx = x + RNG::Ref().between(-1, 1), ry = y + RNG::Ref().between(-1, 1), rt = TYP(pmap[ry][rx]); + int rx = x + sim->rng.between(-1, 1), ry = y + sim->rng.between(-1, 1), rt = TYP(pmap[ry][rx]); if (sim->can_move[PT_PLSM][rt] || rt == PT_NBLE) { j = sim->create_part(-3,rx,ry,PT_PLSM); @@ -83,7 +83,7 @@ static int update(UPDATE_FUNC_ARGS) parts[j].tmp |= 4; } } - parts[i].temp = temp + 1750 + RNG::Ref().between(0, 499); + parts[i].temp = temp + 1750 + sim->rng.between(0, 499); sim->pv[y/CELL][x/CELL] += 50; } } diff --git a/src/simulation/elements/NEUT.cpp b/src/simulation/elements/NEUT.cpp index d3ebdc2a7..2e1289603 100644 --- a/src/simulation/elements/NEUT.cpp +++ b/src/simulation/elements/NEUT.cpp @@ -10,7 +10,7 @@ void Element::Element_NEUT() { Identifier = "DEFAULT_PT_NEUT"; Name = "NEUT"; - Colour = PIXPACK(0x20E0FF); + Colour = 0x20E0FF_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -64,7 +64,7 @@ static int update(UPDATE_FUNC_ARGS) switch (TYP(r)) { case PT_WATR: - if (RNG::Ref().chance(3, 20)) + if (sim->rng.chance(3, 20)) sim->part_change_type(ID(r),x+rx,y+ry,PT_DSTW); case PT_ICEI: case PT_SNOW: @@ -72,11 +72,11 @@ static int update(UPDATE_FUNC_ARGS) parts[i].vy *= 0.995f; break; case PT_PLUT: - if (RNG::Ref().chance(pressureFactor, 1000)) + if (sim->rng.chance(pressureFactor, 1000)) { - if (RNG::Ref().chance(1, 3)) + if (sim->rng.chance(1, 3)) { - sim->create_part(ID(r), x+rx, y+ry, RNG::Ref().chance(2, 3) ? PT_LAVA : PT_URAN); + sim->create_part(ID(r), x+rx, y+ry, sim->rng.chance(2, 3) ? PT_LAVA : PT_URAN); parts[ID(r)].temp = MAX_TEMP; if (parts[ID(r)].type==PT_LAVA) { parts[ID(r)].tmp = 100; @@ -93,89 +93,74 @@ static int update(UPDATE_FUNC_ARGS) Element_FIRE_update(UPDATE_FUNC_SUBCALL_ARGS); } break; -#ifdef SDEUT case PT_DEUT: - if (RNG::Ref().chance(pressureFactor + 1 + (parts[ID(r)].life/100), 1000)) + if (sim->rng.chance(pressureFactor + 1 + (parts[ID(r)].life/100), 1000)) { DeutExplosion(sim, parts[ID(r)].life, x+rx, y+ry, restrict_flt(parts[ID(r)].temp + parts[ID(r)].life*500.0f, MIN_TEMP, MAX_TEMP), PT_NEUT); sim->kill_part(ID(r)); } break; -#else - case PT_DEUT: - if (RNG::Ref().chance(pressureFactor+1, 1000)) - { - create_part(ID(r), x+rx, y+ry, PT_NEUT); - parts[ID(r)].vx = 0.25f*parts[ID(r)].vx + parts[i].vx; - parts[ID(r)].vy = 0.25f*parts[ID(r)].vy + parts[i].vy; - parts[ID(r)].life --; - parts[ID(r)].temp = restrict_flt(parts[ID(r)].temp + parts[ID(r)].life*17.0f, MIN_TEMP, MAX_TEMP); - pv[y/CELL][x/CELL] += 6.0f * CFDS; - - } - break; -#endif case PT_GUNP: - if (RNG::Ref().chance(3, 200)) + if (sim->rng.chance(3, 200)) sim->part_change_type(ID(r),x+rx,y+ry,PT_DUST); break; case PT_DYST: - if (RNG::Ref().chance(3, 200)) + if (sim->rng.chance(3, 200)) sim->part_change_type(ID(r),x+rx,y+ry,PT_YEST); break; case PT_YEST: sim->part_change_type(ID(r),x+rx,y+ry,PT_DYST); break; case PT_PLEX: - if (RNG::Ref().chance(3, 200)) + if (sim->rng.chance(3, 200)) sim->part_change_type(ID(r),x+rx,y+ry,PT_GOO); break; case PT_NITR: - if (RNG::Ref().chance(3, 200)) + if (sim->rng.chance(3, 200)) sim->part_change_type(ID(r),x+rx,y+ry,PT_DESL); break; case PT_PLNT: - if (RNG::Ref().chance(1, 20)) + if (sim->rng.chance(1, 20)) sim->create_part(ID(r), x+rx, y+ry, PT_WOOD); break; case PT_DESL: case PT_OIL: - if (RNG::Ref().chance(3, 200)) + if (sim->rng.chance(3, 200)) sim->part_change_type(ID(r),x+rx,y+ry,PT_GAS); break; case PT_COAL: - if (RNG::Ref().chance(1, 20)) + if (sim->rng.chance(1, 20)) sim->create_part(ID(r), x+rx, y+ry, PT_WOOD); break; case PT_BCOL: - if (RNG::Ref().chance(1, 20)) + if (sim->rng.chance(1, 20)) sim->create_part(ID(r), x+rx, y+ry, PT_SAWD); break; case PT_DUST: - if (RNG::Ref().chance(1, 20)) + if (sim->rng.chance(1, 20)) sim->part_change_type(ID(r), x+rx, y+ry, PT_FWRK); break; case PT_FWRK: - if (RNG::Ref().chance(1, 20)) + if (sim->rng.chance(1, 20)) parts[ID(r)].ctype = PT_DUST; break; case PT_ACID: - if (RNG::Ref().chance(1, 20)) + if (sim->rng.chance(1, 20)) sim->create_part(ID(r), x+rx, y+ry, PT_ISOZ); break; case PT_TTAN: - if (RNG::Ref().chance(1, 20)) + if (sim->rng.chance(1, 20)) { sim->kill_part(i); return 1; } break; case PT_EXOT: - if (RNG::Ref().chance(1, 20)) + if (sim->rng.chance(1, 20)) parts[ID(r)].life = 1500; break; case PT_RFRG: - if (RNG::Ref().chance(1, 2)) + if (sim->rng.chance(1, 2)) sim->create_part(ID(r), x+rx, y+ry, PT_GAS); else sim->create_part(ID(r), x+rx, y+ry, PT_CAUS); @@ -200,9 +185,9 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - float r = RNG::Ref().between(128, 255) / 127.0f; - float a = RNG::Ref().between(0, 359) * 3.14159f / 180.0f; - sim->parts[i].life = RNG::Ref().between(480, 959); + float r = sim->rng.between(128, 255) / 127.0f; + float a = sim->rng.between(0, 359) * 3.14159f / 180.0f; + sim->parts[i].life = sim->rng.between(480, 959); sim->parts[i].vx = r * cosf(a); sim->parts[i].vy = r * sinf(a); } diff --git a/src/simulation/elements/NICE.cpp b/src/simulation/elements/NICE.cpp index 5dd893f24..e1a86a877 100644 --- a/src/simulation/elements/NICE.cpp +++ b/src/simulation/elements/NICE.cpp @@ -4,7 +4,7 @@ void Element::Element_NICE() { Identifier = "DEFAULT_PT_NICE"; Name = "NICE"; - Colour = PIXPACK(0xC0E0FF); + Colour = 0xC0E0FF_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/NITR.cpp b/src/simulation/elements/NITR.cpp index 40515a67d..ab139d60a 100644 --- a/src/simulation/elements/NITR.cpp +++ b/src/simulation/elements/NITR.cpp @@ -4,7 +4,7 @@ void Element::Element_NITR() { Identifier = "DEFAULT_PT_NITR"; Name = "NITR"; - Colour = PIXPACK(0x20E010); + Colour = 0x20E010_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; diff --git a/src/simulation/elements/NONE.cpp b/src/simulation/elements/NONE.cpp index 9ef0cea0a..3e81aa35a 100644 --- a/src/simulation/elements/NONE.cpp +++ b/src/simulation/elements/NONE.cpp @@ -1,12 +1,12 @@ #include "simulation/ElementCommon.h" -static VideoBuffer *iconGen(int wallID, int width, int height); +static std::unique_ptr iconGen(int wallID, Vec2 size); void Element::Element_NONE() { Identifier = "DEFAULT_PT_NONE"; Name = "NONE"; - Colour = PIXPACK(0x000000); + Colour = 0x000000_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -45,17 +45,9 @@ void Element::Element_NONE() IconGenerator = &iconGen; } -static VideoBuffer *iconGen(int wallID, int width, int height) +static std::unique_ptr iconGen(int wallID, Vec2 size) { - VideoBuffer * newTexture = new VideoBuffer(width, height); - - for (int j=3; j<(width-4)/2; j++) - { - newTexture->SetPixel(j+6, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(j+7, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(-j+19, j, 0xFF, 0, 0, 255); - newTexture->SetPixel(-j+20, j, 0xFF, 0, 0, 255); - } - - return newTexture; + auto texture = std::make_unique(size); + texture->BlendChar(size / 2 - Vec2(4, 2), 0xE06C, 0xFF0000_rgb .WithAlpha(0xFF)); + return texture; } diff --git a/src/simulation/elements/NSCN.cpp b/src/simulation/elements/NSCN.cpp index 5eac6ff96..50694736a 100644 --- a/src/simulation/elements/NSCN.cpp +++ b/src/simulation/elements/NSCN.cpp @@ -4,7 +4,7 @@ void Element::Element_NSCN() { Identifier = "DEFAULT_PT_NSCN"; Name = "NSCN"; - Colour = PIXPACK(0x505080); + Colour = 0x505080_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/NTCT.cpp b/src/simulation/elements/NTCT.cpp index df7888467..47c219eb7 100644 --- a/src/simulation/elements/NTCT.cpp +++ b/src/simulation/elements/NTCT.cpp @@ -6,7 +6,7 @@ void Element::Element_NTCT() { Identifier = "DEFAULT_PT_NTCT"; Name = "NTCT"; - Colour = PIXPACK(0x505040); + Colour = 0x505040_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/NWHL.cpp b/src/simulation/elements/NWHL.cpp index 8906b8709..091b0934b 100644 --- a/src/simulation/elements/NWHL.cpp +++ b/src/simulation/elements/NWHL.cpp @@ -6,7 +6,7 @@ void Element::Element_NWHL() { Identifier = "DEFAULT_PT_NWHL"; Name = "WHOL"; - Colour = PIXPACK(0xFFFFFF); + Colour = 0xFFFFFF_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -48,8 +48,8 @@ void Element::Element_NWHL() static int update(UPDATE_FUNC_ARGS) { if (parts[i].tmp) - sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] -= restrict_flt(0.001f*parts[i].tmp, 0.1f, 51.2f); + sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] -= restrict_flt(0.001f*parts[i].tmp, 0.1f, 51.2f); else - sim->gravmap[(y/CELL)*(XRES/CELL)+(x/CELL)] -= 0.1f; + sim->gravmap[(y/CELL)*XCELLS+(x/CELL)] -= 0.1f; return 0; } diff --git a/src/simulation/elements/O2.cpp b/src/simulation/elements/O2.cpp index 67701ed15..62a362bdc 100644 --- a/src/simulation/elements/O2.cpp +++ b/src/simulation/elements/O2.cpp @@ -6,7 +6,7 @@ void Element::Element_O2() { Identifier = "DEFAULT_PT_O2"; Name = "OXYG"; - Colour = PIXPACK(0x80A0FF); + Colour = 0x80A0FF_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -58,30 +58,30 @@ static int update(UPDATE_FUNC_ARGS) if (TYP(r)==PT_FIRE) { - parts[ID(r)].temp += RNG::Ref().between(0, 99); + parts[ID(r)].temp += sim->rng.between(0, 99); if (parts[ID(r)].tmp & 0x01) parts[ID(r)].temp = 3473; parts[ID(r)].tmp |= 2; sim->create_part(i,x,y,PT_FIRE); - parts[i].temp += RNG::Ref().between(0, 99); + parts[i].temp += sim->rng.between(0, 99); parts[i].tmp |= 2; } else if (TYP(r)==PT_PLSM && !(parts[ID(r)].tmp&4)) { sim->create_part(i,x,y,PT_FIRE); - parts[i].temp += RNG::Ref().between(0, 99); + parts[i].temp += sim->rng.between(0, 99); parts[i].tmp |= 2; } } if (parts[i].temp > 9973.15 && sim->pv[y/CELL][x/CELL] > 250.0f) { - int gravPos = ((y/CELL)*(XRES/CELL))+(x/CELL); + int gravPos = ((y/CELL)*XCELLS)+(x/CELL); float gravx = sim->gravx[gravPos]; float gravy = sim->gravy[gravPos]; if (gravx*gravx + gravy*gravy > 400) { - if (RNG::Ref().chance(1, 5)) + if (sim->rng.chance(1, 5)) { int j; sim->create_part(i,x,y,PT_BRMT); @@ -95,7 +95,7 @@ static int update(UPDATE_FUNC_ARGS) parts[j].temp = MAX_TEMP; parts[j].tmp = 0x1; } - rx = x + RNG::Ref().between(-1, 1), ry = y + RNG::Ref().between(-1, 1), r = TYP(pmap[ry][rx]); + rx = x + sim->rng.between(-1, 1), ry = y + sim->rng.between(-1, 1), r = TYP(pmap[ry][rx]); if (sim->can_move[PT_PLSM][r] || r == PT_O2) { j = sim->create_part(-3,rx,ry,PT_PLSM); diff --git a/src/simulation/elements/OIL.cpp b/src/simulation/elements/OIL.cpp index 26e0dfbfa..600b10cb4 100644 --- a/src/simulation/elements/OIL.cpp +++ b/src/simulation/elements/OIL.cpp @@ -4,7 +4,7 @@ void Element::Element_OIL() { Identifier = "DEFAULT_PT_OIL"; Name = "OIL"; - Colour = PIXPACK(0x404010); + Colour = 0x404010_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/PBCN.cpp b/src/simulation/elements/PBCN.cpp index edd6bae1c..3ef66a158 100644 --- a/src/simulation/elements/PBCN.cpp +++ b/src/simulation/elements/PBCN.cpp @@ -8,7 +8,7 @@ void Element::Element_PBCN() { Identifier = "DEFAULT_PT_PBCN"; Name = "PBCN"; - Colour = PIXPACK(0x3B1D0A); + Colour = 0x3B1D0A_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; @@ -34,6 +34,7 @@ void Element::Element_PBCN() Description = "Powered breakable clone."; Properties = TYPE_SOLID | PROP_NOCTYPEDRAW; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -55,7 +56,7 @@ static int update(UPDATE_FUNC_ARGS) { int r, rx, ry, rt; if (!parts[i].tmp2 && sim->pv[y/CELL][x/CELL]>4.0f) - parts[i].tmp2 = RNG::Ref().between(80, 119); + parts[i].tmp2 = sim->rng.between(80, 119); if (parts[i].tmp2) { parts[i].vx += ADVECTION*sim->vx[y/CELL][x/CELL]; @@ -135,9 +136,9 @@ static int update(UPDATE_FUNC_ARGS) for (ry=-1; ry<2; ry++) sim->create_part(-1, x+rx, y+ry, PT_LIFE, parts[i].tmp); - else if (parts[i].ctype!=PT_LIGH || RNG::Ref().chance(1, 30)) + else if (parts[i].ctype!=PT_LIGH || sim->rng.chance(1, 30)) { - int np = sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), TYP(parts[i].ctype)); + int np = sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), TYP(parts[i].ctype)); if (np>-1) { if (parts[i].ctype==PT_LAVA && parts[i].tmp>0 && parts[i].tmpelements[parts[i].tmp].HighTemperatureTransition==PT_LAVA) diff --git a/src/simulation/elements/PCLN.cpp b/src/simulation/elements/PCLN.cpp index 523c76c88..2b4e9363c 100644 --- a/src/simulation/elements/PCLN.cpp +++ b/src/simulation/elements/PCLN.cpp @@ -8,7 +8,7 @@ void Element::Element_PCLN() { Identifier = "DEFAULT_PT_PCLN"; Name = "PCLN"; - Colour = PIXPACK(0x3B3B0A); + Colour = 0x3B3B0A_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; @@ -34,6 +34,7 @@ void Element::Element_PCLN() Description = "Powered clone. When activated, duplicates any particles it touches."; Properties = TYPE_SOLID | PROP_NOCTYPEDRAW; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -126,9 +127,9 @@ static int update(UPDATE_FUNC_ARGS) for (ry=-1; ry<2; ry++) sim->create_part(-1, x+rx, y+ry, PT_LIFE, parts[i].tmp); - else if (parts[i].ctype != PT_LIGH || RNG::Ref().chance(1, 30)) + else if (parts[i].ctype != PT_LIGH || sim->rng.chance(1, 30)) { - int np = sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), TYP(parts[i].ctype)); + int np = sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), TYP(parts[i].ctype)); if (np>=0) { if (parts[i].ctype==PT_LAVA && parts[i].tmp>0 && parts[i].tmpelements[parts[i].tmp].HighTemperatureTransition==PT_LAVA) diff --git a/src/simulation/elements/PHOT.cpp b/src/simulation/elements/PHOT.cpp index 4c7c00c34..8efe2fea4 100644 --- a/src/simulation/elements/PHOT.cpp +++ b/src/simulation/elements/PHOT.cpp @@ -9,7 +9,7 @@ void Element::Element_PHOT() { Identifier = "DEFAULT_PT_PHOT"; Name = "PHOT"; - Colour = PIXPACK(0xFFFFFF); + Colour = 0xFFFFFF_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -63,7 +63,7 @@ static int update(UPDATE_FUNC_ARGS) return 1; } if (parts[i].temp > 506) - if (RNG::Ref().chance(1, 10)) + if (sim->rng.chance(1, 10)) Element_FIRE_update(UPDATE_FUNC_SUBCALL_ARGS); for (rx=-1; rx<2; rx++) for (ry=-1; ry<2; ry++) @@ -73,16 +73,16 @@ static int update(UPDATE_FUNC_ARGS) continue; if (TYP(r)==PT_ISOZ || TYP(r)==PT_ISZS) { - if (RNG::Ref().chance(1, 400)) + if (sim->rng.chance(1, 400)) { parts[i].vx *= 0.90f; parts[i].vy *= 0.90f; sim->create_part(ID(r), x+rx, y+ry, PT_PHOT); - rrr = RNG::Ref().between(0, 359) * 3.14159f / 180.0f; + rrr = sim->rng.between(0, 359) * 3.14159f / 180.0f; if (TYP(r) == PT_ISOZ) - rr = RNG::Ref().between(128, 255) / 127.0f; + rr = sim->rng.between(128, 255) / 127.0f; else - rr = RNG::Ref().between(128, 355) / 127.0f; + rr = sim->rng.between(128, 355) / 127.0f; parts[ID(r)].vx = rr*cosf(rrr); parts[ID(r)].vy = rr*sinf(rrr); sim->pv[y/CELL][x/CELL] -= 15.0f * CFDS; @@ -90,17 +90,17 @@ static int update(UPDATE_FUNC_ARGS) } else if((TYP(r) == PT_QRTZ || TYP(r) == PT_PQRT) && !ry && !rx)//if on QRTZ { - float a = RNG::Ref().between(0, 359) * 3.14159f / 180.0f; + float a = sim->rng.between(0, 359) * 3.14159f / 180.0f; parts[i].vx = 3.0f*cosf(a); parts[i].vy = 3.0f*sinf(a); if(parts[i].ctype == 0x3FFFFFFF) - parts[i].ctype = 0x1F << RNG::Ref().between(0, 25); + parts[i].ctype = 0x1F << sim->rng.between(0, 25); if (parts[i].life) parts[i].life++; //Delay death } else if(TYP(r) == PT_BGLA && !ry && !rx)//if on BGLA { - float a = RNG::Ref().between(-50, 50) * 0.001f; + float a = sim->rng.between(-50, 50) * 0.001f; float rx = cosf(a), ry = sinf(a), vx, vy; vx = rx * parts[i].vx + ry * parts[i].vy; vy = rx * parts[i].vy - ry * parts[i].vx; @@ -109,8 +109,8 @@ static int update(UPDATE_FUNC_ARGS) } else if (TYP(r) == PT_FILT && parts[ID(r)].tmp==9) { - parts[i].vx += ((float)RNG::Ref().between(-500, 500))/1000.0f; - parts[i].vy += ((float)RNG::Ref().between(-500, 500))/1000.0f; + parts[i].vx += ((float)sim->rng.between(-500, 500))/1000.0f; + parts[i].vy += ((float)sim->rng.between(-500, 500))/1000.0f; } } return 0; @@ -153,10 +153,10 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - float a = RNG::Ref().between(0, 7) * 0.78540f; + float a = sim->rng.between(0, 7) * 0.78540f; sim->parts[i].vx = 3.0f * cosf(a); sim->parts[i].vy = 3.0f * sinf(a); - int Element_FILT_interactWavelengths(Particle* cpart, int origWl); + int Element_FILT_interactWavelengths(Simulation *sim, Particle* cpart, int origWl); if (TYP(sim->pmap[y][x]) == PT_FILT) - sim->parts[i].ctype = Element_FILT_interactWavelengths(&sim->parts[ID(sim->pmap[y][x])], sim->parts[i].ctype); + sim->parts[i].ctype = Element_FILT_interactWavelengths(sim, &sim->parts[ID(sim->pmap[y][x])], sim->parts[i].ctype); } diff --git a/src/simulation/elements/PIPE.cpp b/src/simulation/elements/PIPE.cpp index 31399dcc0..4e6446814 100644 --- a/src/simulation/elements/PIPE.cpp +++ b/src/simulation/elements/PIPE.cpp @@ -1,5 +1,7 @@ #include "simulation/ElementCommon.h" +extern const std::array, 8> Element_PIPE_offsets; +void Element_PIPE_transformPatchOffsets(Particle &part, const std::array &offsetMap); int Element_PIPE_update(UPDATE_FUNC_ARGS); int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS); void Element_PIPE_transfer_pipe_to_part(Simulation * sim, Particle *pipe, Particle *part, bool STOR); @@ -12,7 +14,7 @@ void Element::Element_PIPE() { Identifier = "DEFAULT_PT_PIPE"; Name = "PIPE"; - Colour = PIXPACK(0x444444); + Colour = 0x444444_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; @@ -39,6 +41,7 @@ void Element::Element_PIPE() Description = "PIPE, moves particles around. Once the BRCK generates, erase some for the exit. Then the PIPE generates and is usable."; Properties = TYPE_SOLID|PROP_LIFE_DEC; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -77,40 +80,21 @@ constexpr int PPIP_TMPFLAG_TRIGGER_OFF = 0x08000000; constexpr int PPIP_TMPFLAG_TRIGGER_ON = 0x10000000; constexpr int PPIP_TMPFLAG_TRIGGERS = 0x1C000000; -signed char pos_1_rx[] = { -1,-1,-1, 0, 0, 1, 1, 1 }; -signed char pos_1_ry[] = { -1, 0, 1,-1, 1,-1, 0, 1 }; +const std::array, 8> Element_PIPE_offsets = {{ + { -1, -1 }, + { -1, 0 }, + { -1, 1 }, + { 0, -1 }, + { 0, 1 }, + { 1, -1 }, + { 1, 0 }, + { 1, 1 }, +}}; -static void transformPatch(Particle &part, const int (&patch)[8]) +void Element_PIPE_transformPatchOffsets(Particle &part, const std::array &offsetMap) { - if (part.tmp & 0x00000200) part.tmp = (part.tmp & 0xFFFFE3FF) | (patch[(part.tmp & 0x00001C00) >> 10] << 10); - if (part.tmp & 0x00002000) part.tmp = (part.tmp & 0xFFFE3FFF) | (patch[(part.tmp & 0x0001C000) >> 14] << 14); -} - -void Element_PIPE_patchR(Particle &part) -{ - // 035 -> 210 - // 1 6 -> 4 3 - // 247 -> 765 - const int patchR[] = { 2, 4, 7, 1, 6, 0, 3, 5 }; - transformPatch(part, patchR); -} - -void Element_PIPE_patchH(Particle &part) -{ - // 035 -> 530 - // 1 6 -> 6 1 - // 247 -> 742 - const int patchH[] = { 5, 6, 7, 3, 4, 0, 1, 2 }; - transformPatch(part, patchH); -} - -void Element_PIPE_patchV(Particle &part) -{ - // 035 -> 247 - // 1 6 -> 1 6 - // 247 -> 035 - const int patchV[] = { 2, 1, 0, 4, 3, 7, 6, 5 }; - transformPatch(part, patchV); + if (part.tmp & 0x00000200) part.tmp = (part.tmp & 0xFFFFE3FF) | (offsetMap[(part.tmp & 0x00001C00) >> 10] << 10); + if (part.tmp & 0x00002000) part.tmp = (part.tmp & 0xFFFE3FFF) | (offsetMap[(part.tmp & 0x0001C000) >> 14] << 14); } static unsigned int prevColor(unsigned int flags) @@ -256,11 +240,11 @@ int Element_PIPE_update(UPDATE_FUNC_ARGS) if (nt)//there is something besides PIPE around current particle { - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); rnd = rndstore&7; //rndstore = rndstore>>3; - rx = pos_1_rx[rnd]; - ry = pos_1_ry[rnd]; + rx = Element_PIPE_offsets[rnd].X; + ry = Element_PIPE_offsets[rnd].Y; if (BOUNDS_CHECK) { r = pmap[y+ry][x+rx]; @@ -381,9 +365,10 @@ int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS) cpart->tmp = tpart.tmp3; cpart->ctype = tpart.tmp4; - *colr = PIXR(ren->sim->elements[t].Colour); - *colg = PIXG(ren->sim->elements[t].Colour); - *colb = PIXB(ren->sim->elements[t].Colour); + RGB colour = ren->sim->elements[t].Colour; + *colr = colour.Red; + *colg = colour.Green; + *colb = colour.Blue; if (ren->sim->elements[t].Graphics) { (*(ren->sim->elements[t].Graphics))(ren, cpart, nx, ny, pixel_mode, cola, colr, colg, colb, firea, firer, fireg, fireb); @@ -396,9 +381,6 @@ int Element_PIPE_graphics(GRAPHICS_FUNC_ARGS) // Restore original particle data. *cpart = tpart; } - //*colr = PIXR(elements[t].pcolors); - //*colg = PIXG(elements[t].pcolors); - //*colb = PIXB(elements[t].pcolors); } else { @@ -494,7 +476,7 @@ static void pushParticle(Simulation * sim, int i, int count, int original) if( !(sim->parts[i].tmp&0x200) ) { //normal random push - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); // RAND_MAX is at least 32767 on all platforms i.e. pow(8,5)-1 // so can go 5 cycles without regenerating rndstore // (although now we use our own randomizer so maybe should reevaluate all the rndstore usages in every element) @@ -502,8 +484,8 @@ static void pushParticle(Simulation * sim, int i, int count, int original) { rnd = rndstore&7; rndstore = rndstore>>3; - rx = pos_1_rx[rnd]; - ry = pos_1_ry[rnd]; + rx = Element_PIPE_offsets[rnd].X; + ry = Element_PIPE_offsets[rnd].Y; if (BOUNDS_CHECK) { r = sim->pmap[y+ry][x+rx]; @@ -538,7 +520,7 @@ static void pushParticle(Simulation * sim, int i, int count, int original) else //predefined 1 pixel thick pipe movement { int coords = 7 - ((sim->parts[i].tmp>>10)&7); - r = sim->pmap[y+ pos_1_ry[coords]][x+ pos_1_rx[coords]]; + r = sim->pmap[y+ Element_PIPE_offsets[coords].Y][x+ Element_PIPE_offsets[coords].X]; if ((TYP(r)==PT_PIPE || TYP(r) == PT_PPIP) && (sim->parts[ID(r)].tmp&PFLAG_COLORS) != notctype && !TYP(sim->parts[ID(r)].ctype)) { transfer_pipe_to_pipe(sim->parts+i, sim->parts+(ID(r)), false); @@ -564,8 +546,8 @@ static void pushParticle(Simulation * sim, int i, int count, int original) } else if (!r) //Move particles out of pipe automatically, much faster at ends { - rx = pos_1_rx[coords]; - ry = pos_1_ry[coords]; + rx = Element_PIPE_offsets[coords].X; + ry = Element_PIPE_offsets[coords].Y; np = sim->create_part(-1,x+rx,y+ry,TYP(sim->parts[i].ctype)); if (np!=-1) { diff --git a/src/simulation/elements/PLEX.cpp b/src/simulation/elements/PLEX.cpp index 7e6b9565c..01140a6fe 100644 --- a/src/simulation/elements/PLEX.cpp +++ b/src/simulation/elements/PLEX.cpp @@ -4,7 +4,7 @@ void Element::Element_PLEX() { Identifier = "DEFAULT_PT_PLEX"; Name = "C-4"; - Colour = PIXPACK(0xD080E0); + Colour = 0xD080E0_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; diff --git a/src/simulation/elements/PLNT.cpp b/src/simulation/elements/PLNT.cpp index d7a01d68a..6f3777c4b 100644 --- a/src/simulation/elements/PLNT.cpp +++ b/src/simulation/elements/PLNT.cpp @@ -8,7 +8,7 @@ void Element::Element_PLNT() { Identifier = "DEFAULT_PT_PLNT"; Name = "PLNT"; - Colour = PIXPACK(0x0CAC00); + Colour = 0x0CAC00_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -59,7 +59,7 @@ static int update(UPDATE_FUNC_ARGS) switch (TYP(r)) { case PT_WATR: - if (RNG::Ref().chance(1, 50)) + if (sim->rng.chance(1, 50)) { np = sim->create_part(ID(r),x+rx,y+ry,PT_PLNT); if (np<0) continue; @@ -67,7 +67,7 @@ static int update(UPDATE_FUNC_ARGS) } break; case PT_LAVA: - if (RNG::Ref().chance(1, 50)) + if (sim->rng.chance(1, 50)) { sim->part_change_type(i,x,y,PT_FIRE); parts[i].life = 4; @@ -75,14 +75,14 @@ static int update(UPDATE_FUNC_ARGS) break; case PT_SMKE: case PT_CO2: - if (RNG::Ref().chance(1, 50)) + if (sim->rng.chance(1, 50)) { sim->kill_part(ID(r)); - parts[i].life = RNG::Ref().between(60, 119); + parts[i].life = sim->rng.between(60, 119); } break; case PT_WOOD: - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); if (surround_space && !(rndstore%4) && parts[i].tmp==1) { rndstore >>= 3; diff --git a/src/simulation/elements/PLSM.cpp b/src/simulation/elements/PLSM.cpp index e9519e49e..b737a8a0a 100644 --- a/src/simulation/elements/PLSM.cpp +++ b/src/simulation/elements/PLSM.cpp @@ -8,7 +8,7 @@ void Element::Element_PLSM() { Identifier = "DEFAULT_PT_PLSM"; Name = "PLSM"; - Colour = PIXPACK(0xBB99FF); + Colour = 0xBB99FF_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -52,10 +52,10 @@ void Element::Element_PLSM() static int graphics(GRAPHICS_FUNC_ARGS) { - auto color = Renderer::plasmaTableAt(cpart->life); - *colr = PIXR(color); - *colg = PIXG(color); - *colb = PIXB(color); + RGB color = Renderer::plasmaTableAt(cpart->life); + *colr = color.Red; + *colg = color.Green; + *colb = color.Blue; *firea = 255; *firer = *colr; @@ -70,5 +70,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].life = RNG::Ref().between(50, 199); + sim->parts[i].life = sim->rng.between(50, 199); } diff --git a/src/simulation/elements/PLUT.cpp b/src/simulation/elements/PLUT.cpp index 3fd8c4d87..cb6ccb351 100644 --- a/src/simulation/elements/PLUT.cpp +++ b/src/simulation/elements/PLUT.cpp @@ -6,7 +6,7 @@ void Element::Element_PLUT() { Identifier = "DEFAULT_PT_PLUT"; Name = "PLUT"; - Colour = PIXPACK(0x407020); + Colour = 0x407020_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -48,7 +48,7 @@ void Element::Element_PLUT() static int update(UPDATE_FUNC_ARGS) { - if (RNG::Ref().chance(1, 100) && RNG::Ref().chance(int(5.0f*sim->pv[y/CELL][x/CELL]), 1000)) + if (sim->rng.chance(1, 100) && sim->rng.chance(int(5.0f*sim->pv[y/CELL][x/CELL]), 1000)) { sim->create_part(i, x, y, PT_NEUT); } diff --git a/src/simulation/elements/POLO.cpp b/src/simulation/elements/POLO.cpp index d89d49e42..6f4def48f 100644 --- a/src/simulation/elements/POLO.cpp +++ b/src/simulation/elements/POLO.cpp @@ -7,7 +7,7 @@ void Element::Element_POLO() { Identifier = "DEFAULT_PT_POLO"; Name = "POLO"; - Colour = PIXPACK(0x506030); + Colour = 0x506030_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -56,7 +56,7 @@ static int update(UPDATE_FUNC_ARGS) int r = sim->photons[y][x]; if (parts[i].tmp < LIMIT && !parts[i].life) { - if (RNG::Ref().chance(1, 10000) && !parts[i].tmp) + if (sim->rng.chance(1, 10000) && !parts[i].tmp) { int s = sim->create_part(-3, x, y, PT_NEUT); if (s >= 0) @@ -69,7 +69,7 @@ static int update(UPDATE_FUNC_ARGS) } } - if (r && RNG::Ref().chance(1, 100)) + if (r && sim->rng.chance(1, 100)) { int s = sim->create_part(-3, x, y, PT_NEUT); if (s >= 0) diff --git a/src/simulation/elements/PPIP.cpp b/src/simulation/elements/PPIP.cpp index 69579c465..e245dbbc7 100644 --- a/src/simulation/elements/PPIP.cpp +++ b/src/simulation/elements/PPIP.cpp @@ -7,7 +7,7 @@ void Element::Element_PPIP() { Identifier = "DEFAULT_PT_PPIP"; Name = "PPIP"; - Colour = PIXPACK(0x444466); + Colour = 0x444466_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; @@ -34,6 +34,7 @@ void Element::Element_PPIP() Description = "Powered version of PIPE, use PSCN/NSCN to Activate/Deactivate."; Properties = TYPE_SOLID|PROP_LIFE_DEC; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/PQRT.cpp b/src/simulation/elements/PQRT.cpp index 99962a0f0..7a2cfc8db 100644 --- a/src/simulation/elements/PQRT.cpp +++ b/src/simulation/elements/PQRT.cpp @@ -8,7 +8,7 @@ void Element::Element_PQRT() { Identifier = "DEFAULT_PT_PQRT"; Name = "PQRT"; - Colour = PIXPACK(0x88BBBB); + Colour = 0x88BBBB_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -51,5 +51,5 @@ void Element::Element_PQRT() static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].tmp2 = RNG::Ref().between(0, 10); + sim->parts[i].tmp2 = sim->rng.between(0, 10); } diff --git a/src/simulation/elements/PROT.cpp b/src/simulation/elements/PROT.cpp index e3adfbb12..5d0c7a2a3 100644 --- a/src/simulation/elements/PROT.cpp +++ b/src/simulation/elements/PROT.cpp @@ -9,7 +9,7 @@ void Element::Element_PROT() { Identifier = "DEFAULT_PT_PROT"; Name = "PROT"; - Colour = PIXPACK(0x990000); + Colour = 0x990000_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -74,7 +74,7 @@ static int update(UPDATE_FUNC_ARGS) break; } case PT_DEUT: - if (RNG::Ref().chance(-((int)sim->pv[y / CELL][x / CELL] - 4) + (parts[uID].life / 100), 200)) + if (sim->rng.chance(-((int)sim->pv[y / CELL][x / CELL] - 4) + (parts[uID].life / 100), 200)) { DeutImplosion(sim, parts[uID].life, x, y, restrict_flt(parts[uID].temp + parts[uID].life * 500, MIN_TEMP, MAX_TEMP), PT_PROT); sim->kill_part(uID); @@ -82,7 +82,7 @@ static int update(UPDATE_FUNC_ARGS) break; case PT_LCRY: //Powered LCRY reaction: PROT->PHOT - if (parts[uID].life > 5 && RNG::Ref().chance(1, 10)) + if (parts[uID].life > 5 && sim->rng.chance(1, 10)) { sim->part_change_type(i, x, y, PT_PHOT); parts[i].life *= 2; @@ -149,7 +149,7 @@ static int update(UPDATE_FUNC_ARGS) element = PT_CO2; else element = PT_NBLE; - newID = sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), element); + newID = sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), element); if (newID >= 0) parts[newID].temp = restrict_flt(100.0f*parts[i].tmp, MIN_TEMP, MAX_TEMP); sim->kill_part(i); @@ -209,7 +209,7 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - float a = RNG::Ref().between(0, 35) * 0.17453f; + float a = sim->rng.between(0, 35) * 0.17453f; sim->parts[i].life = 680; sim->parts[i].vx = 2.0f * cosf(a); sim->parts[i].vy = 2.0f * sinf(a); diff --git a/src/simulation/elements/PRTI.cpp b/src/simulation/elements/PRTI.cpp index d53f13ca4..519457791 100644 --- a/src/simulation/elements/PRTI.cpp +++ b/src/simulation/elements/PRTI.cpp @@ -9,7 +9,7 @@ void Element::Element_PRTI() { Identifier = "DEFAULT_PT_PRTI"; Name = "PRTI"; - Colour = PIXPACK(0xEB5917); + Colour = 0xEB5917_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -119,22 +119,22 @@ static int update(UPDATE_FUNC_ARGS) if (fe) { int orbd[4] = {0, 0, 0, 0}; //Orbital distances int orbl[4] = {0, 0, 0, 0}; //Orbital locations - if (!sim->parts[i].life) parts[i].life = RNG::Ref().gen(); - if (!sim->parts[i].ctype) parts[i].ctype = RNG::Ref().gen(); + if (!sim->parts[i].life) parts[i].life = sim->rng.gen(); + if (!sim->parts[i].ctype) parts[i].ctype = sim->rng.gen(); sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl); for (int r = 0; r < 4; r++) { if (orbd[r]>1) { orbd[r] -= 12; if (orbd[r]<1) { - orbd[r] = RNG::Ref().between(128, 255); - orbl[r] = RNG::Ref().between(0, 254); + orbd[r] = sim->rng.between(128, 255); + orbl[r] = sim->rng.between(0, 254); } else { orbl[r] += 2; orbl[r] = orbl[r]%255; } } else { - orbd[r] = RNG::Ref().between(128, 255); - orbl[r] = RNG::Ref().between(0, 254); + orbd[r] = sim->rng.between(128, 255); + orbl[r] = sim->rng.between(0, 254); } } sim->orbitalparts_set(&parts[i].life, &parts[i].ctype, orbd, orbl); diff --git a/src/simulation/elements/PRTO.cpp b/src/simulation/elements/PRTO.cpp index 48a07862f..a701aa2fa 100644 --- a/src/simulation/elements/PRTO.cpp +++ b/src/simulation/elements/PRTO.cpp @@ -7,7 +7,7 @@ void Element::Element_PRTO() { Identifier = "DEFAULT_PT_PRTO"; Name = "PRTO"; - Colour = PIXPACK(0x0020EB); + Colour = 0x0020EB_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -74,7 +74,7 @@ static int update(UPDATE_FUNC_ARGS) fe = 1; for ( nnx =0 ; nnx<80; nnx++) { - int randomness = (count + RNG::Ref().between(-1, 1) + 4) % 8;//add -1,0,or 1 to count + int randomness = (count + sim->rng.between(-1, 1) + 4) % 8;//add -1,0,or 1 to count if (sim->portalp[parts[i].tmp][randomness][nnx].type==PT_SPRK)// TODO: make it look better, spark creation { sim->create_part(-1,x+1,y,PT_SPRK); @@ -142,15 +142,15 @@ static int update(UPDATE_FUNC_ARGS) if (fe) { int orbd[4] = {0, 0, 0, 0}; //Orbital distances int orbl[4] = {0, 0, 0, 0}; //Orbital locations - if (!sim->parts[i].life) parts[i].life = RNG::Ref().gen(); - if (!sim->parts[i].ctype) parts[i].ctype = RNG::Ref().gen(); + if (!sim->parts[i].life) parts[i].life = sim->rng.gen(); + if (!sim->parts[i].ctype) parts[i].ctype = sim->rng.gen(); sim->orbitalparts_get(parts[i].life, parts[i].ctype, orbd, orbl); for (r = 0; r < 4; r++) { if (orbd[r]<254) { orbd[r] += 16; if (orbd[r]>254) { orbd[r] = 0; - orbl[r] = RNG::Ref().between(0, 254); + orbl[r] = sim->rng.between(0, 254); } else { @@ -161,7 +161,7 @@ static int update(UPDATE_FUNC_ARGS) //orbl[r] = orbl[r]%255; } else { orbd[r] = 0; - orbl[r] = RNG::Ref().between(0, 254); + orbl[r] = sim->rng.between(0, 254); } } sim->orbitalparts_set(&parts[i].life, &parts[i].ctype, orbd, orbl); diff --git a/src/simulation/elements/PSCN.cpp b/src/simulation/elements/PSCN.cpp index a99451807..c8696acc1 100644 --- a/src/simulation/elements/PSCN.cpp +++ b/src/simulation/elements/PSCN.cpp @@ -4,7 +4,7 @@ void Element::Element_PSCN() { Identifier = "DEFAULT_PT_PSCN"; Name = "PSCN"; - Colour = PIXPACK(0x805050); + Colour = 0x805050_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/PSNS.cpp b/src/simulation/elements/PSNS.cpp index 65997816f..2f92afa99 100644 --- a/src/simulation/elements/PSNS.cpp +++ b/src/simulation/elements/PSNS.cpp @@ -6,7 +6,7 @@ void Element::Element_PSNS() { Identifier = "DEFAULT_PT_PSNS"; Name = "PSNS"; - Colour = PIXPACK(0xDB2020); + Colour = 0xDB2020_rgb; MenuVisible = 1; MenuSection = SC_SENSOR; Enabled = 1; diff --git a/src/simulation/elements/PSTE.cpp b/src/simulation/elements/PSTE.cpp index 09b3791c3..11d01268a 100644 --- a/src/simulation/elements/PSTE.cpp +++ b/src/simulation/elements/PSTE.cpp @@ -4,7 +4,7 @@ void Element::Element_PSTE() { Identifier = "DEFAULT_PT_PSTE"; Name = "PSTE"; - Colour = PIXPACK(0xAA99AA); + Colour = 0xAA99AA_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/PSTN.cpp b/src/simulation/elements/PSTN.cpp index 920542e8a..225ade2f6 100644 --- a/src/simulation/elements/PSTN.cpp +++ b/src/simulation/elements/PSTN.cpp @@ -12,7 +12,7 @@ void Element::Element_PSTN() { Identifier = "DEFAULT_PT_PSTN"; Name = "PSTN"; - Colour = PIXPACK(0xAA9999); + Colour = 0xAA9999_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; @@ -39,6 +39,7 @@ void Element::Element_PSTN() Description = "Piston, extends and pushes particles."; Properties = TYPE_SOLID; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/PSTS.cpp b/src/simulation/elements/PSTS.cpp index 816f046f0..2f15452e5 100644 --- a/src/simulation/elements/PSTS.cpp +++ b/src/simulation/elements/PSTS.cpp @@ -4,7 +4,7 @@ void Element::Element_PSTS() { Identifier = "DEFAULT_PT_PSTS"; Name = "PSTS"; - Colour = PIXPACK(0x776677); + Colour = 0x776677_rgb; MenuVisible = 0; MenuSection = SC_CRACKER; Enabled = 1; diff --git a/src/simulation/elements/PTCT.cpp b/src/simulation/elements/PTCT.cpp index bc5dadb69..c85c51295 100644 --- a/src/simulation/elements/PTCT.cpp +++ b/src/simulation/elements/PTCT.cpp @@ -6,7 +6,7 @@ void Element::Element_PTCT() { Identifier = "DEFAULT_PT_PTCT"; Name = "PTCT"; - Colour = PIXPACK(0x405050); + Colour = 0x405050_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/PTNM.cpp b/src/simulation/elements/PTNM.cpp index 34acfe507..045d4a14a 100644 --- a/src/simulation/elements/PTNM.cpp +++ b/src/simulation/elements/PTNM.cpp @@ -8,7 +8,7 @@ void Element::Element_PTNM() { Identifier = "DEFAULT_PT_PTNM"; Name = "PTNM"; - Colour = PIXPACK(0xD5E0EB); + Colour = 0xD5E0EB_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -109,7 +109,7 @@ static void hygn_reactions(int hygn1_id, UPDATE_FUNC_ARGS) } // Cold fusion: 2 hydrogen > 500 C has a chance to fuse - if (rt == PT_H2 && RNG::Ref().chance(1, 1000) && parts[ID(r)].temp > 500.0f + 273.15f && parts[hygn1_id].temp > 500.0f + 273.15f) + if (rt == PT_H2 && sim->rng.chance(1, 1000) && parts[ID(r)].temp > 500.0f + 273.15f && parts[hygn1_id].temp > 500.0f + 273.15f) { sim->part_change_type(ID(r), x + rx, y + ry, PT_NBLE); sim->part_change_type(hygn1_id, (int)(parts[hygn1_id].x + 0.5f), (int)(parts[hygn1_id].y + 0.5f), PT_NEUT); @@ -125,7 +125,7 @@ static void hygn_reactions(int hygn1_id, UPDATE_FUNC_ARGS) parts[j].temp = parts[ID(r)].temp; parts[j].tmp = 0x1; } - if (RNG::Ref().chance(1, 10)) + if (sim->rng.chance(1, 10)) { int j = sim->create_part(-3, x + rx, y + ry, PT_ELEC); if (j > -1) @@ -212,7 +212,7 @@ static int update(UPDATE_FUNC_ARGS) float prob = std::min(1.0f, parts[i].temp / (273.15f + 1500.0f)); prob *= prob; - if (RNG::Ref().uniform01() <= prob) + if (sim->rng.uniform01() <= prob) { switch (rt) { @@ -266,6 +266,6 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - if (RNG::Ref().chance(1, 15)) + if (sim->rng.chance(1, 15)) sim->parts[i].tmp = 1; } diff --git a/src/simulation/elements/PUMP.cpp b/src/simulation/elements/PUMP.cpp index 27531cacf..c1dda5ab2 100644 --- a/src/simulation/elements/PUMP.cpp +++ b/src/simulation/elements/PUMP.cpp @@ -7,7 +7,7 @@ void Element::Element_PUMP() { Identifier = "DEFAULT_PT_PUMP"; Name = "PUMP"; - Colour = PIXPACK(0x0A0A3B); + Colour = 0x0A0A3B_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; diff --git a/src/simulation/elements/PVOD.cpp b/src/simulation/elements/PVOD.cpp index 457c06bf9..98f039d81 100644 --- a/src/simulation/elements/PVOD.cpp +++ b/src/simulation/elements/PVOD.cpp @@ -7,7 +7,7 @@ void Element::Element_PVOD() { Identifier = "DEFAULT_PT_PVOD"; Name = "PVOD"; - Colour = PIXPACK(0x792020); + Colour = 0x792020_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; diff --git a/src/simulation/elements/QRTZ.cpp b/src/simulation/elements/QRTZ.cpp index ac3a1f937..5acf98545 100644 --- a/src/simulation/elements/QRTZ.cpp +++ b/src/simulation/elements/QRTZ.cpp @@ -8,7 +8,7 @@ void Element::Element_QRTZ() { Identifier = "DEFAULT_PT_QRTZ"; Name = "QRTZ"; - Colour = PIXPACK(0xAADDDD); + Colour = 0xAADDDD_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -74,7 +74,7 @@ int Element_QRTZ_update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - else if (TYP(r)==PT_SLTW && RNG::Ref().chance(1, 500)) + else if (TYP(r)==PT_SLTW && sim->rng.chance(1, 500)) { sim->kill_part(ID(r)); parts[i].tmp++; @@ -87,7 +87,7 @@ int Element_QRTZ_update(UPDATE_FUNC_ARGS) int rnd, sry, srx; for (trade = 0; trade < 9; trade++) { - rnd = RNG::Ref().gen() % 0x3FF; + rnd = sim->rng.gen() % 0x3FF; rx = (rnd%5)-2; srx = (rnd%3)-1; rnd >>= 3; @@ -104,9 +104,9 @@ int Element_QRTZ_update(UPDATE_FUNC_ARGS) { parts[np].temp = parts[i].temp; parts[np].tmp2 = parts[i].tmp2; - if (RNG::Ref().chance(1, 2)) + if (sim->rng.chance(1, 2)) { - parts[np].tmp2 = std::clamp(parts[np].tmp2 + RNG::Ref().between(-1, 1), 0, 10); + parts[np].tmp2 = std::clamp(parts[np].tmp2 + sim->rng.between(-1, 1), 0, 10); } parts[i].tmp--; if (t == PT_PQRT) @@ -114,11 +114,11 @@ int Element_QRTZ_update(UPDATE_FUNC_ARGS) // If PQRT is stationary and has started growing particles of QRTZ, the PQRT is basically part of a new QRTZ crystal. So turn it back into QRTZ so that it behaves more like part of the crystal. sim->part_change_type(i,x,y,PT_QRTZ); } - if (RNG::Ref().chance(1, 2)) + if (sim->rng.chance(1, 2)) { parts[np].tmp=-1;//dead qrtz } - else if (!parts[i].tmp && RNG::Ref().chance(1, 15)) + else if (!parts[i].tmp && sim->rng.chance(1, 15)) { parts[i].tmp=-1; } @@ -164,6 +164,6 @@ int Element_QRTZ_graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].tmp2 = RNG::Ref().between(0, 10); + sim->parts[i].tmp2 = sim->rng.between(0, 10); sim->parts[i].tmp3 = int(sim->pv[y/CELL][x/CELL] * 64); } diff --git a/src/simulation/elements/RBDM.cpp b/src/simulation/elements/RBDM.cpp index 395274b81..911b006fb 100644 --- a/src/simulation/elements/RBDM.cpp +++ b/src/simulation/elements/RBDM.cpp @@ -4,7 +4,7 @@ void Element::Element_RBDM() { Identifier = "DEFAULT_PT_RBDM"; Name = "RBDM"; - Colour = PIXPACK(0xCCCCCC); + Colour = 0xCCCCCC_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; diff --git a/src/simulation/elements/RFGL.cpp b/src/simulation/elements/RFGL.cpp index 16a95ae69..ee8372368 100644 --- a/src/simulation/elements/RFGL.cpp +++ b/src/simulation/elements/RFGL.cpp @@ -6,7 +6,7 @@ void Element::Element_RFGL() { Identifier = "DEFAULT_PT_RFGL"; Name = "RFGL"; - Colour = PIXPACK(0x84C2CF); + Colour = 0x84C2CF_rgb; MenuVisible = 0; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/RFRG.cpp b/src/simulation/elements/RFRG.cpp index f072b3a88..0c728ef16 100644 --- a/src/simulation/elements/RFRG.cpp +++ b/src/simulation/elements/RFRG.cpp @@ -6,7 +6,7 @@ void Element::Element_RFRG() { Identifier = "DEFAULT_PT_RFRG"; Name = "RFRG"; - Colour = PIXPACK(0x72D2D4); + Colour = 0x72D2D4_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; diff --git a/src/simulation/elements/RIME.cpp b/src/simulation/elements/RIME.cpp index 1bee19ea1..1884db1ec 100644 --- a/src/simulation/elements/RIME.cpp +++ b/src/simulation/elements/RIME.cpp @@ -6,7 +6,7 @@ void Element::Element_RIME() { Identifier = "DEFAULT_PT_RIME"; Name = "RIME"; - Colour = PIXPACK(0xCCCCCC); + Colour = 0xCCCCCC_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -30,7 +30,7 @@ void Element::Element_RIME() DefaultProperties.temp = -30.0f + 273.15f; HeatConduct = 100; - Description = "Solid, created when steam cools rapidly and goes through sublimation."; + Description = "Solid, created when steam cools rapidly and goes through deposition, skipping the liquid phase."; Properties = TYPE_SOLID; @@ -59,7 +59,7 @@ static int update(UPDATE_FUNC_ARGS) if (TYP(r)==PT_SPRK) { sim->part_change_type(i,x,y,PT_FOG); - parts[i].life = RNG::Ref().between(60, 119); + parts[i].life = sim->rng.between(60, 119); } else if (TYP(r)==PT_FOG&&parts[ID(r)].life>0) { diff --git a/src/simulation/elements/ROCK.cpp b/src/simulation/elements/ROCK.cpp index 96239433d..adcb79dae 100644 --- a/src/simulation/elements/ROCK.cpp +++ b/src/simulation/elements/ROCK.cpp @@ -7,7 +7,7 @@ void Element::Element_ROCK() { Identifier = "DEFAULT_PT_ROCK"; Name = "ROCK"; - Colour = PIXPACK(0x727272); + Colour = 0x727272_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -69,5 +69,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].tmp2 = RNG::Ref().between(0, 10); + sim->parts[i].tmp2 = sim->rng.between(0, 10); } diff --git a/src/simulation/elements/RPEL.cpp b/src/simulation/elements/RPEL.cpp index a1dfdf351..0bde82704 100644 --- a/src/simulation/elements/RPEL.cpp +++ b/src/simulation/elements/RPEL.cpp @@ -6,7 +6,7 @@ void Element::Element_RPEL() { Identifier = "DEFAULT_PT_RPEL"; Name = "RPEL"; - Colour = PIXPACK(0x99CC00); + Colour = 0x99CC00_rgb; MenuVisible = 1; MenuSection = SC_FORCE; Enabled = 1; @@ -52,8 +52,8 @@ static int update(UPDATE_FUNC_ARGS) int r, rx, ry, ri; for(ri = 0; ri <= 10; ri++) { - rx = RNG::Ref().between(-10, 10); - ry = RNG::Ref().between(-10, 10); + rx = sim->rng.between(-10, 10); + ry = sim->rng.between(-10, 10); if (x+rx >= 0 && x+rx < XRES && y+ry >= 0 && y+ry < YRES && (rx || ry)) { r = pmap[y+ry][x+rx]; diff --git a/src/simulation/elements/SALT.cpp b/src/simulation/elements/SALT.cpp index 9e4f0c89a..1b7dd410e 100644 --- a/src/simulation/elements/SALT.cpp +++ b/src/simulation/elements/SALT.cpp @@ -4,7 +4,7 @@ void Element::Element_SALT() { Identifier = "DEFAULT_PT_SALT"; Name = "SALT"; - Colour = PIXPACK(0xFFFFFF); + Colour = 0xFFFFFF_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/SAND.cpp b/src/simulation/elements/SAND.cpp index 14c6e50db..6ce02d35c 100644 --- a/src/simulation/elements/SAND.cpp +++ b/src/simulation/elements/SAND.cpp @@ -4,7 +4,7 @@ void Element::Element_SAND() { Identifier = "DEFAULT_PT_SAND"; Name = "SAND"; - Colour = PIXPACK(0xFFD090); + Colour = 0xFFD090_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/SAWD.cpp b/src/simulation/elements/SAWD.cpp index cbbd8378d..19ad799c4 100644 --- a/src/simulation/elements/SAWD.cpp +++ b/src/simulation/elements/SAWD.cpp @@ -4,7 +4,7 @@ void Element::Element_SAWD() { Identifier = "DEFAULT_PT_SAWD"; Name = "SAWD"; - Colour = PIXPACK(0xF0F0A0); + Colour = 0xF0F0A0_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/SHLD1.cpp b/src/simulation/elements/SHLD1.cpp index 5dc8b8ed8..056fd1233 100644 --- a/src/simulation/elements/SHLD1.cpp +++ b/src/simulation/elements/SHLD1.cpp @@ -6,7 +6,7 @@ void Element::Element_SHLD1() { Identifier = "DEFAULT_PT_SHLD1"; Name = "SHLD"; - Colour = PIXPACK(0xAAAAAA); + Colour = 0xAAAAAA_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -57,7 +57,7 @@ static int update(UPDATE_FUNC_ARGS) continue; else if (TYP(r)==PT_SPRK&&parts[i].life==0) { - if (RNG::Ref().chance(11, 40)) + if (sim->rng.chance(11, 40)) { sim->part_change_type(i,x,y,PT_SHLD2); parts[i].life = 7; @@ -72,7 +72,7 @@ static int update(UPDATE_FUNC_ARGS) } } } - else if (TYP(r) == PT_SHLD3 && RNG::Ref().chance(2, 5)) + else if (TYP(r) == PT_SHLD3 && sim->rng.chance(2, 5)) { sim->part_change_type(i,x,y,PT_SHLD2); parts[i].life = 7; diff --git a/src/simulation/elements/SHLD2.cpp b/src/simulation/elements/SHLD2.cpp index d9b081d1c..e1492507c 100644 --- a/src/simulation/elements/SHLD2.cpp +++ b/src/simulation/elements/SHLD2.cpp @@ -6,7 +6,7 @@ void Element::Element_SHLD2() { Identifier = "DEFAULT_PT_SHLD2"; Name = "SHD2"; - Colour = PIXPACK(0x777777); + Colour = 0x777777_rgb; MenuVisible = 0; MenuSection = SC_CRACKER2; Enabled = 1; @@ -61,7 +61,7 @@ static int update(UPDATE_FUNC_ARGS) } else if (TYP(r)==PT_SPRK&&parts[i].life==0) { - if (RNG::Ref().chance(1, 8)) + if (sim->rng.chance(1, 8)) { sim->part_change_type(i,x,y,PT_SHLD3); parts[i].life = 7; @@ -77,7 +77,7 @@ static int update(UPDATE_FUNC_ARGS) } } } - else if (TYP(r) == PT_SHLD4 && RNG::Ref().chance(2, 5)) + else if (TYP(r) == PT_SHLD4 && sim->rng.chance(2, 5)) { sim->part_change_type(i,x,y,PT_SHLD3); parts[i].life = 7; diff --git a/src/simulation/elements/SHLD3.cpp b/src/simulation/elements/SHLD3.cpp index 882a5f56f..d41ea4fec 100644 --- a/src/simulation/elements/SHLD3.cpp +++ b/src/simulation/elements/SHLD3.cpp @@ -6,7 +6,7 @@ void Element::Element_SHLD3() { Identifier = "DEFAULT_PT_SHLD3"; Name = "SHD3"; - Colour = PIXPACK(0x444444); + Colour = 0x444444_rgb; MenuVisible = 0; MenuSection = SC_CRACKER2; Enabled = 1; @@ -55,7 +55,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) { - if (RNG::Ref().chance(1, 2500)) + if (sim->rng.chance(1, 2500)) { np = sim->create_part(-1,x+rx,y+ry,PT_SHLD1); if (np<0) continue; @@ -71,7 +71,7 @@ static int update(UPDATE_FUNC_ARGS) } else if (TYP(r)==PT_SPRK&&parts[i].life==0) { - if (RNG::Ref().chance(3, 500)) + if (sim->rng.chance(3, 500)) { sim->part_change_type(i,x,y,PT_SHLD4); parts[i].life = 7; diff --git a/src/simulation/elements/SHLD4.cpp b/src/simulation/elements/SHLD4.cpp index e57d18c5a..3169db74f 100644 --- a/src/simulation/elements/SHLD4.cpp +++ b/src/simulation/elements/SHLD4.cpp @@ -6,7 +6,7 @@ void Element::Element_SHLD4() { Identifier = "DEFAULT_PT_SHLD4"; Name = "SHD4"; - Colour = PIXPACK(0x212121); + Colour = 0x212121_rgb; MenuVisible = 0; MenuSection = SC_CRACKER2; Enabled = 1; @@ -55,7 +55,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) { - if (RNG::Ref().chance(1, 5500)) + if (sim->rng.chance(1, 5500)) { np = sim->create_part(-1,x+rx,y+ry,PT_SHLD1); if (np<0) continue; diff --git a/src/simulation/elements/SING.cpp b/src/simulation/elements/SING.cpp index 5ec9e0e28..378527a69 100644 --- a/src/simulation/elements/SING.cpp +++ b/src/simulation/elements/SING.cpp @@ -7,7 +7,7 @@ void Element::Element_SING() { Identifier = "DEFAULT_PT_SING"; Name = "SING"; - Colour = PIXPACK(0x242424); + Colour = 0x242424_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -49,7 +49,7 @@ void Element::Element_SING() static int update(UPDATE_FUNC_ARGS) { - int r, rx, ry, cry, crx, nb, spawncount; + int r, rx, ry, cry, crx, spawncount; int singularity = -parts[i].life; float angle, v; @@ -71,16 +71,17 @@ static int update(UPDATE_FUNC_ARGS) crx = (x/CELL)+rx; for (ry=-1; ry<2; ry++) { cry = (y/CELL)+ry; - if (cry >= 0 && crx >= 0 && crx < (XRES/CELL) && cry < (YRES/CELL)) { + if (cry >= 0 && crx >= 0 && crx < XCELLS && cry < YCELLS) { sim->pv[cry][crx] += (float)parts[i].tmp; } } } spawncount = std::abs(parts[i].tmp); - spawncount = (spawncount>255) ? 3019 : int(std::pow((double)(spawncount/8), 2)*M_PI); + spawncount = (spawncount>255) ? 3019 : int(std::pow((double)(spawncount/8), 2)*TPT_PI_FLT); for (int j = 0;j < spawncount; j++) { - switch (RNG::Ref().gen() % 3) + auto nb = -1; + switch (sim->rng.gen() % 3) { case 0: nb = sim->create_part(-3, x, y, PT_PHOT); @@ -93,10 +94,10 @@ static int update(UPDATE_FUNC_ARGS) break; } if (nb!=-1) { - parts[nb].life = RNG::Ref().between(0, 299); + parts[nb].life = sim->rng.between(0, 299); parts[nb].temp = MAX_TEMP/2; - angle = RNG::Ref().uniform01()*2.0f*M_PI; - v = RNG::Ref().uniform01()*5.0f; + angle = sim->rng.uniform01()*2.0f*TPT_PI_FLT; + v = sim->rng.uniform01()*5.0f; parts[nb].vx = v*cosf(angle); parts[nb].vy = v*sinf(angle); } @@ -113,7 +114,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)!=PT_DMND&& RNG::Ref().chance(1, 3)) + if (TYP(r)!=PT_DMND&& sim->rng.chance(1, 3)) { if (TYP(r)==PT_SING && parts[ID(r)].life >10) { @@ -125,11 +126,11 @@ static int update(UPDATE_FUNC_ARGS) { if (parts[i].life+3 > 255) { - if (parts[ID(r)].type!=PT_SING && RNG::Ref().chance(1, 1000)) + if (parts[ID(r)].type!=PT_SING && sim->rng.chance(1, 1000)) { int np; np = sim->create_part(ID(r),x+rx,y+ry,PT_SING); - parts[np].life = RNG::Ref().between(60, 109); + parts[np].life = sim->rng.between(60, 109); } continue; } @@ -145,5 +146,5 @@ static int update(UPDATE_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].life = RNG::Ref().between(60, 109); + sim->parts[i].life = sim->rng.between(60, 109); } diff --git a/src/simulation/elements/SLCN.cpp b/src/simulation/elements/SLCN.cpp index c740f3de9..efc7873bd 100644 --- a/src/simulation/elements/SLCN.cpp +++ b/src/simulation/elements/SLCN.cpp @@ -8,7 +8,7 @@ void Element::Element_SLCN() { Identifier = "DEFAULT_PT_SLCN"; Name = "SLCN"; - Colour = PIXPACK(0xBCCDDF); + Colour = 0xBCCDDF_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -49,33 +49,33 @@ void Element::Element_SLCN() Create = &create; } -static const int SLCN_COLOUR[16] = { - PIXPACK(0x5A6679), PIXPACK(0x6878A1), PIXPACK(0xABBFDD), PIXPACK(0x838490), - PIXPACK(0xBCCDDF), PIXPACK(0x82A0D2), PIXPACK(0x5B6680), PIXPACK(0x232C3B), - PIXPACK(0x485067), PIXPACK(0x8B9AB6), PIXPACK(0xADB1C1), PIXPACK(0xC3C6D1), - PIXPACK(0x8594AD), PIXPACK(0x262F47), PIXPACK(0xA9AEBC), PIXPACK(0xC2E1F7), +static const RGB SLCN_COLOUR[16] = { + 0x5A6679_rgb, 0x6878A1_rgb, 0xABBFDD_rgb, 0x838490_rgb, + 0xBCCDDF_rgb, 0x82A0D2_rgb, 0x5B6680_rgb, 0x232C3B_rgb, + 0x485067_rgb, 0x8B9AB6_rgb, 0xADB1C1_rgb, 0xC3C6D1_rgb, + 0x8594AD_rgb, 0x262F47_rgb, 0xA9AEBC_rgb, 0xC2E1F7_rgb, }; -static void initSparkles(Particle &part) +static void initSparkles(Simulation *sim, Particle &part) { // bits 31-20: phase increment (randomised to a value between 1 and 9) // bits 19-16: next colour index // bits 15-12: current colour index // bits 11-00: phase - part.tmp = RNG::Ref().between(0x100000, 0x9FFFFF); + part.tmp = sim->rng.between(0x100000, 0x9FFFFF); } static int update(UPDATE_FUNC_ARGS) { if (!parts[i].tmp) { - initSparkles(parts[i]); + initSparkles(sim, parts[i]); } int phase = (parts[i].tmp & 0xFFF) + ((parts[i].tmp >> 20) & 0xFFF); if (phase & 0x1000) { // discard current, current <- next, next <- random, wrap phase - parts[i].tmp = (parts[i].tmp & 0xFFF00000) | (phase & 0xFFF) | (RNG::Ref().between(0, 15) << 16) | ((parts[i].tmp >> 4) & 0xF000); + parts[i].tmp = (parts[i].tmp & 0xFFF00000) | (phase & 0xFFF) | (sim->rng.between(0, 15) << 16) | ((parts[i].tmp >> 4) & 0xF000); } else { @@ -108,19 +108,19 @@ static int update(UPDATE_FUNC_ARGS) static int graphics(GRAPHICS_FUNC_ARGS) { - int curr_colour = SLCN_COLOUR[(cpart->tmp >> 12) & 15]; + RGB curr_colour = SLCN_COLOUR[(cpart->tmp >> 12) & 15]; if (cpart->tmp & 0x800) // mix with next colour if phase is at least halfway there { - int next_colour = SLCN_COLOUR[(cpart->tmp >> 16) & 15]; - curr_colour = PIXRGB( - (PIXR(curr_colour) + PIXR(next_colour)) / 2, - (PIXG(curr_colour) + PIXG(next_colour)) / 2, - (PIXB(curr_colour) + PIXB(next_colour)) / 2 + RGB next_colour = SLCN_COLOUR[(cpart->tmp >> 16) & 15]; + curr_colour = RGB( + (curr_colour.Red + next_colour.Red) / 2, + (curr_colour.Green + next_colour.Green) / 2, + (curr_colour.Blue + next_colour.Blue) / 2 ); } - *colr = PIXR(curr_colour); - *colg = PIXG(curr_colour); - *colb = PIXB(curr_colour); + *colr = curr_colour.Red; + *colg = curr_colour.Green; + *colb = curr_colour.Blue; int rnd = (cpart->tmp & 0xFFFF) * ((cpart->tmp >> 16) & 0xFFFF); if (!(rnd % 887)) @@ -137,5 +137,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - initSparkles(sim->parts[i]); + initSparkles(sim, sim->parts[i]); } diff --git a/src/simulation/elements/SLTW.cpp b/src/simulation/elements/SLTW.cpp index da58ba4d0..420e781a6 100644 --- a/src/simulation/elements/SLTW.cpp +++ b/src/simulation/elements/SLTW.cpp @@ -6,7 +6,7 @@ void Element::Element_SLTW() { Identifier = "DEFAULT_PT_SLTW"; Name = "SLTW"; - Colour = PIXPACK(0x4050F0); + Colour = 0x4050F0_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -53,19 +53,19 @@ static int update(UPDATE_FUNC_ARGS) if (BOUNDS_CHECK && (rx || ry)) { r = pmap[y+ry][x+rx]; - switch TYP(r) + switch (TYP(r)) { case PT_SALT: - if (RNG::Ref().chance(1, 2000)) + if (sim->rng.chance(1, 2000)) sim->part_change_type(ID(r),x+rx,y+ry,PT_SLTW); break; case PT_PLNT: - if (RNG::Ref().chance(1, 40)) + if (sim->rng.chance(1, 40)) sim->kill_part(ID(r)); break; case PT_RBDM: case PT_LRBD: - if ((sim->legacy_enable||parts[i].temp>(273.15f+12.0f)) && RNG::Ref().chance(1, 100)) + if ((sim->legacy_enable||parts[i].temp>(273.15f+12.0f)) && sim->rng.chance(1, 100)) { sim->part_change_type(i,x,y,PT_FIRE); parts[i].life = 4; @@ -76,7 +76,7 @@ static int update(UPDATE_FUNC_ARGS) if (parts[ID(r)].ctype!=PT_WATR) { sim->kill_part(ID(r)); - if (RNG::Ref().chance(1, 30)) + if (sim->rng.chance(1, 30)) { sim->kill_part(i); return 1; diff --git a/src/simulation/elements/SMKE.cpp b/src/simulation/elements/SMKE.cpp index 82fde602e..d3a1c4a86 100644 --- a/src/simulation/elements/SMKE.cpp +++ b/src/simulation/elements/SMKE.cpp @@ -6,7 +6,7 @@ void Element::Element_SMKE() { Identifier = "DEFAULT_PT_SMKE"; Name = "SMKE"; - Colour = PIXPACK(0x222222); + Colour = 0x222222_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; diff --git a/src/simulation/elements/SNOW.cpp b/src/simulation/elements/SNOW.cpp index 2436a3a07..02382cabf 100644 --- a/src/simulation/elements/SNOW.cpp +++ b/src/simulation/elements/SNOW.cpp @@ -6,7 +6,7 @@ void Element::Element_SNOW() { Identifier = "DEFAULT_PT_SNOW"; Name = "SNOW"; - Colour = PIXPACK(0xC0E0FF); + Colour = 0xC0E0FF_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -60,7 +60,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_SALT || TYP(r)==PT_SLTW) && RNG::Ref().chance(1, 333)) + if ((TYP(r)==PT_SALT || TYP(r)==PT_SLTW) && sim->rng.chance(1, 333)) { sim->part_change_type(i,x,y,PT_SLTW); sim->part_change_type(ID(r),x+rx,y+ry,PT_SLTW); diff --git a/src/simulation/elements/SOAP.cpp b/src/simulation/elements/SOAP.cpp index bed250fb7..e950a646d 100644 --- a/src/simulation/elements/SOAP.cpp +++ b/src/simulation/elements/SOAP.cpp @@ -8,7 +8,7 @@ void Element::Element_SOAP() { Identifier = "DEFAULT_PT_SOAP"; Name = "SOAP"; - Colour = PIXPACK(0xF5F5DC); + Colour = 0xF5F5DC_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; diff --git a/src/simulation/elements/SPAWN.cpp b/src/simulation/elements/SPAWN.cpp index 2641151ac..051375ae8 100644 --- a/src/simulation/elements/SPAWN.cpp +++ b/src/simulation/elements/SPAWN.cpp @@ -7,7 +7,7 @@ void Element::Element_SPAWN() { Identifier = "DEFAULT_PT_SPAWN"; Name = "SPWN"; - Colour = PIXPACK(0xAAAAAA); + Colour = 0xAAAAAA_rgb; MenuVisible = 0; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/SPAWN2.cpp b/src/simulation/elements/SPAWN2.cpp index db5d66bd7..1606b86df 100644 --- a/src/simulation/elements/SPAWN2.cpp +++ b/src/simulation/elements/SPAWN2.cpp @@ -7,7 +7,7 @@ void Element::Element_SPAWN2() { Identifier = "DEFAULT_PT_SPAWN2"; Name = "SPWN2"; - Colour = PIXPACK(0xAAAAAA); + Colour = 0xAAAAAA_rgb; MenuVisible = 0; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/SPNG.cpp b/src/simulation/elements/SPNG.cpp index e44687ad5..3dcc4aa83 100644 --- a/src/simulation/elements/SPNG.cpp +++ b/src/simulation/elements/SPNG.cpp @@ -7,7 +7,7 @@ void Element::Element_SPNG() { Identifier = "DEFAULT_PT_SPNG"; Name = "SPNG"; - Colour = PIXPACK(0xFFBE30); + Colour = 0xFFBE30_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -59,36 +59,36 @@ static int update(UPDATE_FUNC_ARGS) if (BOUNDS_CHECK && (rx || ry)) { r = pmap[y+ry][x+rx]; - switch TYP(r) + switch (TYP(r)) { case PT_WATR: case PT_DSTW: case PT_FRZW: - if (parts[i].liferng.chance(500, absorbChanceDenom)) { parts[i].life++; sim->kill_part(ID(r)); } break; case PT_SLTW: - if (parts[i].liferng.chance(50, absorbChanceDenom)) { parts[i].life++; - if (RNG::Ref().chance(3, 4)) + if (sim->rng.chance(3, 4)) sim->kill_part(ID(r)); else sim->part_change_type(ID(r), x+rx, y+ry, PT_SALT); } break; case PT_CBNW: - if (parts[i].liferng.chance(100, absorbChanceDenom)) { parts[i].life++; sim->part_change_type(ID(r), x+rx, y+ry, PT_CO2); } break; case PT_PSTE: - if (parts[i].liferng.chance(20, absorbChanceDenom)) { parts[i].life++; sim->create_part(ID(r), x+rx, y+ry, PT_CLST); @@ -113,8 +113,8 @@ static int update(UPDATE_FUNC_ARGS) } for ( trade = 0; trade<9; trade ++) { - rx = RNG::Ref().between(-2, 2); - ry = RNG::Ref().between(-2, 2); + rx = sim->rng.between(-2, 2); + ry = sim->rng.between(-2, 2); if (BOUNDS_CHECK && (rx || ry)) { r = pmap[y+ry][x+rx]; diff --git a/src/simulation/elements/SPRK.cpp b/src/simulation/elements/SPRK.cpp index 6389e5c5d..cc88c7a62 100644 --- a/src/simulation/elements/SPRK.cpp +++ b/src/simulation/elements/SPRK.cpp @@ -8,7 +8,7 @@ void Element::Element_SPRK() { Identifier = "DEFAULT_PT_SPRK"; Name = "SPRK"; - Colour = PIXPACK(0xFFFF80); + Colour = 0xFFFF80_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; @@ -34,6 +34,7 @@ void Element::Element_SPRK() Description = "Electricity. The basis of all electronics in TPT, travels along wires and other conductive elements."; Properties = TYPE_SOLID|PROP_LIFE_DEC; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -102,7 +103,7 @@ static int update(UPDATE_FUNC_ARGS) case PT_NBLE: if (parts[i].life<=1 && !(parts[i].tmp&0x1)) { - parts[i].life = RNG::Ref().between(50, 199); + parts[i].life = sim->rng.between(50, 199); sim->part_change_type(i,x,y,PT_PLSM); parts[i].ctype = PT_NBLE; if (parts[i].temp > 5273.15) @@ -121,17 +122,17 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (r) continue; - if (parts[i].tmp>4 && RNG::Ref().chance(1, parts[i].tmp*parts[i].tmp/20+6)) + if (parts[i].tmp>4 && sim->rng.chance(1, parts[i].tmp*parts[i].tmp/20+6)) { int p = sim->create_part(-1, x+rx*2, y+ry*2, PT_LIGH); if (p!=-1) { - parts[p].life = RNG::Ref().between(0, 2+parts[i].tmp/15) + parts[i].tmp/7; + parts[p].life = sim->rng.between(0, 2+parts[i].tmp/15) + parts[i].tmp/7; if (parts[i].life>60) parts[i].life=60; parts[p].temp=parts[p].life*parts[i].tmp/2.5; parts[p].tmp2=1; - parts[p].tmp=int(atan2(-ry, (float)rx)/M_PI*360); + parts[p].tmp=int(atan2(-ry, (float)rx)/TPT_PI_FLT*360); parts[i].temp-=parts[i].tmp*2+parts[i].temp/5; // slight self-cooling if (fabs(sim->pv[y/CELL][x/CELL])!=0.0f) { @@ -154,7 +155,7 @@ static int update(UPDATE_FUNC_ARGS) continue; if (TYP(r)==PT_DSTW || TYP(r)==PT_SLTW || TYP(r)==PT_WATR) { - int rndstore = RNG::Ref().gen()%100; + int rndstore = sim->rng.gen()%100; if (!rndstore) sim->part_change_type(ID(r),x+rx,y+ry,PT_O2); else if (3 > rndstore) @@ -164,7 +165,7 @@ static int update(UPDATE_FUNC_ARGS) break; case PT_TUNG: if(parts[i].temp < 3595.0){ - parts[i].temp += RNG::Ref().between(-4, 15); + parts[i].temp += sim->rng.between(-4, 15); } default: break; diff --git a/src/simulation/elements/STKM.cpp b/src/simulation/elements/STKM.cpp index 00c39c354..79e758731 100644 --- a/src/simulation/elements/STKM.cpp +++ b/src/simulation/elements/STKM.cpp @@ -14,7 +14,7 @@ void Element::Element_STKM() { Identifier = "DEFAULT_PT_STKM"; Name = "STKM"; - Colour = PIXPACK(0xFFE0A0); + Colour = 0xFFE0A0_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -42,6 +42,7 @@ void Element::Element_STKM() Description = "Stickman. Don't kill him! Control with the arrow keys."; Properties = PROP_NOCTYPEDRAW; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; @@ -97,7 +98,10 @@ static void changeType(ELEMENT_CHANGETYPE_FUNC_ARGS) sim->player.spwn = 0; } -#define INBOND(x, y) ((x)>=0 && (y)>=0 && (x)= 0 && y >= 0 && x < XRES && y < YRES; +} int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) { @@ -161,8 +165,8 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) break; } - gvx += sim->gravx[((int)parts[i].y/CELL)*(XRES/CELL)+((int)parts[i].x/CELL)]; - gvy += sim->gravy[((int)parts[i].y/CELL)*(XRES/CELL)+((int)parts[i].x/CELL)]; + gvx += sim->gravx[((int)parts[i].y/CELL)*XCELLS+((int)parts[i].x/CELL)]; + gvy += sim->gravy[((int)parts[i].y/CELL)*XCELLS+((int)parts[i].x/CELL)]; float mvx = gvx; float mvy = gvy; @@ -247,7 +251,7 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) bool moved = false; if (dl>dr) { - if (INBOND(playerp->legs[4], playerp->legs[5]) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) + if (INBOND(int(playerp->legs[4]), int(playerp->legs[5])) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) { playerp->accs[2] = -3*mvy-3*mvx; playerp->accs[3] = 3*mvx-3*mvy; @@ -258,7 +262,7 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) } else { - if (INBOND(playerp->legs[12], playerp->legs[13]) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL)) + if (INBOND(int(playerp->legs[12]), int(playerp->legs[13])) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL)) { playerp->accs[6] = -3*mvy-3*mvx; playerp->accs[7] = 3*mvx-3*mvy; @@ -297,7 +301,7 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) bool moved = false; if (dllegs[4], playerp->legs[5]) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) + if (INBOND(int(playerp->legs[4]), int(playerp->legs[5])) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) { playerp->accs[2] = 3*mvy-3*mvx; playerp->accs[3] = -3*mvx-3*mvy; @@ -308,7 +312,7 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) } else { - if (INBOND(playerp->legs[12], playerp->legs[13]) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL)) + if (INBOND(int(playerp->legs[12]), int(playerp->legs[13])) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL)) { playerp->accs[6] = 3*mvy-3*mvx; playerp->accs[7] = -3*mvx-3*mvy; @@ -374,8 +378,8 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) } } } - else if ((INBOND(playerp->legs[4], playerp->legs[5]) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) || - (INBOND(playerp->legs[12], playerp->legs[13]) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL))) + else if ((INBOND(int(playerp->legs[4]), int(playerp->legs[5])) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) || + (INBOND(int(playerp->legs[12]), int(playerp->legs[13])) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL))) { parts[i].vx -= 4*mvx; parts[i].vy -= 4*mvy; @@ -387,10 +391,10 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) } //Charge detector wall if foot inside - if (INBOND((int)(playerp->legs[4]+0.5)/CELL, (int)(playerp->legs[5]+0.5)/CELL) && + if (INBOND(int(playerp->legs[4]+0.5)/CELL, int(playerp->legs[5]+0.5)/CELL) && sim->bmap[(int)(playerp->legs[5]+0.5)/CELL][(int)(playerp->legs[4]+0.5)/CELL]==WL_DETECT) sim->set_emap((int)playerp->legs[4]/CELL, (int)playerp->legs[5]/CELL); - if (INBOND((int)(playerp->legs[12]+0.5)/CELL, (int)(playerp->legs[13]+0.5)/CELL) && + if (INBOND(int(playerp->legs[12]+0.5)/CELL, int(playerp->legs[13]+0.5)/CELL) && sim->bmap[(int)(playerp->legs[13]+0.5)/CELL][(int)(playerp->legs[12]+0.5)/CELL]==WL_DETECT) sim->set_emap((int)(playerp->legs[12]+0.5)/CELL, (int)(playerp->legs[13]+0.5)/CELL); @@ -441,7 +445,7 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) //Spawn if (((int)(playerp->comm)&0x08) == 0x08) { - ry -= 2 * RNG::Ref().between(0, 1) + 1; + ry -= 2 * sim->rng.between(0, 1) + 1; r = pmap[ry][rx]; if (sim->elements[TYP(r)].Properties&TYPE_SOLID) { @@ -477,7 +481,7 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) { if (playerp->elem == PT_PHOT) { - int random = abs((RNG::Ref().between(-1, 1)))*3; + int random = abs((sim->rng.between(-1, 1)))*3; if (random==0) { sim->kill_part(np); @@ -496,9 +500,9 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) int angle; int power = 100; if (gvx!=0 || gvy!=0) - angle = int(atan2(mvx, mvy)*180.0f/M_PI); + angle = int(atan2(mvx, mvy)*180.0f/TPT_PI_FLT); else - angle = RNG::Ref().between(0, 359); + angle = sim->rng.between(0, 359); if (((int)playerp->pcomm)&0x01) angle += 180; if (angle>360) @@ -506,7 +510,7 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) if (angle<0) angle+=360; parts[np].tmp = angle; - parts[np].life = RNG::Ref().between(0, 1+power/15) + power/7; + parts[np].life = sim->rng.between(0, 1+power/15) + power/7; parts[np].temp = parts[np].life*power/2.5; parts[np].tmp2 = 1; } @@ -547,27 +551,27 @@ int Element_STKM_run_stickman(playerst *playerp, UPDATE_FUNC_ARGS) playerp->legs[8] += (playerp->legs[8]-parts[i].x)*d; playerp->legs[9] += (playerp->legs[9]-parts[i].y)*d; - if (INBOND(playerp->legs[4], playerp->legs[5]) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) + if (INBOND(int(playerp->legs[4]), int(playerp->legs[5])) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) { playerp->legs[4] = playerp->legs[6]; playerp->legs[5] = playerp->legs[7]; } - if (INBOND(playerp->legs[12], playerp->legs[13]) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL)) + if (INBOND(int(playerp->legs[12]), int(playerp->legs[13])) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL)) { playerp->legs[12] = playerp->legs[14]; playerp->legs[13] = playerp->legs[15]; } //This makes stick man "pop" from obstacles - if (INBOND(playerp->legs[4], playerp->legs[5]) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) + if (INBOND(int(playerp->legs[4]), int(playerp->legs[5])) && !sim->eval_move(t, int(playerp->legs[4]), int(playerp->legs[5]), NULL)) { float t; t = playerp->legs[4]; playerp->legs[4] = playerp->legs[6]; playerp->legs[6] = t; t = playerp->legs[5]; playerp->legs[5] = playerp->legs[7]; playerp->legs[7] = t; } - if (INBOND(playerp->legs[12], playerp->legs[13]) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL)) + if (INBOND(int(playerp->legs[12]), int(playerp->legs[13])) && !sim->eval_move(t, int(playerp->legs[12]), int(playerp->legs[13]), NULL)) { float t; t = playerp->legs[12]; playerp->legs[12] = playerp->legs[14]; playerp->legs[14] = t; @@ -629,7 +633,7 @@ void Element_STKM_interact(Simulation *sim, playerst *playerp, int i, int x, int { if (TYP(r)==PT_SPRK && playerp->elem!=PT_LIGH) //If on charge { - sim->parts[i].life -= RNG::Ref().between(32, 51); + sim->parts[i].life -= sim->rng.between(32, 51); } if (sim->elements[TYP(r)].HeatConduct && (TYP(r)!=PT_HSWC||sim->parts[ID(r)].life==10) && ((playerp->elem!=PT_LIGH && sim->parts[ID(r)].temp>=323) || sim->parts[ID(r)].temp<=243) && (!playerp->rocketBoots || TYP(r)!=PT_PLSM)) diff --git a/src/simulation/elements/STKM2.cpp b/src/simulation/elements/STKM2.cpp index dd1501ffb..2ee01e931 100644 --- a/src/simulation/elements/STKM2.cpp +++ b/src/simulation/elements/STKM2.cpp @@ -12,7 +12,7 @@ void Element::Element_STKM2() { Identifier = "DEFAULT_PT_STKM2"; Name = "STK2"; - Colour = PIXPACK(0x6464FF); + Colour = 0x6464FF_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -40,6 +40,7 @@ void Element::Element_STKM2() Description = "Second stickman. Don't kill him! Control with wasd."; Properties = PROP_NOCTYPEDRAW; + CarriesTypeIn = 1U << FIELD_CTYPE; LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/STNE.cpp b/src/simulation/elements/STNE.cpp index a2cad3952..b31311170 100644 --- a/src/simulation/elements/STNE.cpp +++ b/src/simulation/elements/STNE.cpp @@ -4,7 +4,7 @@ void Element::Element_STNE() { Identifier = "DEFAULT_PT_STNE"; Name = "STNE"; - Colour = PIXPACK(0xA0A0A0); + Colour = 0xA0A0A0_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; diff --git a/src/simulation/elements/STOR.cpp b/src/simulation/elements/STOR.cpp index 88ae0f95b..1ef590ed5 100644 --- a/src/simulation/elements/STOR.cpp +++ b/src/simulation/elements/STOR.cpp @@ -9,7 +9,7 @@ void Element::Element_STOR() { Identifier = "DEFAULT_PT_STOR"; Name = "STOR"; - Colour = PIXPACK(0x50DFDF); + Colour = 0x50DFDF_rgb; MenuVisible = 1; MenuSection = SC_POWERED; Enabled = 1; @@ -35,6 +35,7 @@ void Element::Element_STOR() Description = "Storage. Captures and stores a single particle. Releases when charged with PSCN, also passes to PIPE."; Properties = TYPE_SOLID | PROP_NOCTYPEDRAW; + CarriesTypeIn = (1U << FIELD_CTYPE) | (1U << FIELD_TMP); LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/SWCH.cpp b/src/simulation/elements/SWCH.cpp index 6e84b9004..1050ce7aa 100644 --- a/src/simulation/elements/SWCH.cpp +++ b/src/simulation/elements/SWCH.cpp @@ -7,7 +7,7 @@ void Element::Element_SWCH() { Identifier = "DEFAULT_PT_SWCH"; Name = "SWCH"; - Colour = PIXPACK(0x103B11); + Colour = 0x103B11_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/TESC.cpp b/src/simulation/elements/TESC.cpp index 4333ee154..136e8a3c0 100644 --- a/src/simulation/elements/TESC.cpp +++ b/src/simulation/elements/TESC.cpp @@ -6,7 +6,7 @@ void Element::Element_TESC() { Identifier = "DEFAULT_PT_TESC"; Name = "TESC"; - Colour = PIXPACK(0x707040); + Colour = 0x707040_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/THDR.cpp b/src/simulation/elements/THDR.cpp index e716317a2..b420d6185 100644 --- a/src/simulation/elements/THDR.cpp +++ b/src/simulation/elements/THDR.cpp @@ -7,7 +7,7 @@ void Element::Element_THDR() { Identifier = "DEFAULT_PT_THDR"; Name = "THDR"; - Colour = PIXPACK(0xFFFFA0); + Colour = 0xFFFFA0_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; @@ -70,9 +70,9 @@ static int update(UPDATE_FUNC_ARGS) else if (rt!=PT_CLNE&&rt!=PT_THDR&&rt!=PT_SPRK&&rt!=PT_DMND&&rt!=PT_FIRE) { sim->pv[y/CELL][x/CELL] += 100.0f; - if (sim->legacy_enable && RNG::Ref().chance(1, 200)) + if (sim->legacy_enable && sim->rng.chance(1, 200)) { - parts[i].life = RNG::Ref().between(120, 169); + parts[i].life = sim->rng.between(120, 169); sim->part_change_type(i,x,y,PT_FIRE); } else diff --git a/src/simulation/elements/THRM.cpp b/src/simulation/elements/THRM.cpp index c421f0269..2a8eaf57b 100644 --- a/src/simulation/elements/THRM.cpp +++ b/src/simulation/elements/THRM.cpp @@ -4,7 +4,7 @@ void Element::Element_THRM() { Identifier = "DEFAULT_PT_THRM"; Name = "THRM"; - Colour = PIXPACK(0xA08090); + Colour = 0xA08090_rgb; MenuVisible = 1; MenuSection = SC_EXPLOSIVE; Enabled = 1; diff --git a/src/simulation/elements/TRON.cpp b/src/simulation/elements/TRON.cpp index cbeb989a5..4c9c7a24f 100644 --- a/src/simulation/elements/TRON.cpp +++ b/src/simulation/elements/TRON.cpp @@ -12,7 +12,7 @@ void Element::Element_TRON() { Identifier = "DEFAULT_PT_TRON"; Name = "TRON"; - Colour = PIXPACK(0xA9FF00); + Colour = 0xA9FF00_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; @@ -74,12 +74,12 @@ void Element::Element_TRON() * .ctype Contains the colour, lost on save, regenerated using hue tmp (bits 7 - 16) */ -#define TRON_HEAD 1 -#define TRON_NOGROW 2 -#define TRON_WAIT 4 //it was just created, so WAIT a frame -#define TRON_NODIE 8 -#define TRON_DEATH 16 //Crashed, now dying -#define TRON_NORANDOM 65536 +constexpr auto TRON_HEAD = UINT32_C(0x00000001); +constexpr auto TRON_NOGROW = UINT32_C(0x00000002); +constexpr auto TRON_WAIT = UINT32_C(0x00000004); //it was just created, so WAIT a frame +constexpr auto TRON_NODIE = UINT32_C(0x00000008); +constexpr auto TRON_DEATH = UINT32_C(0x00000010); //Crashed, now dying +constexpr auto TRON_NORANDOM = UINT32_C(0x00010000); int tron_rx[4] = {-1, 0, 1, 0}; int tron_ry[4] = { 0,-1, 0, 1}; unsigned int tron_colours[32]; @@ -109,7 +109,7 @@ static int update(UPDATE_FUNC_ARGS) int originaldir = direction; //random turn - int random = RNG::Ref().between(0, 339); + int random = sim->rng.between(0, 339); if ((random==1 || random==3) && !(parts[i].tmp & TRON_NORANDOM)) { //randomly turn left(3) or right(1) @@ -133,7 +133,7 @@ static int update(UPDATE_FUNC_ARGS) } else { - seconddir = (direction + (RNG::Ref().between(0, 1)*2)+1)% 4; + seconddir = (direction + (sim->rng.between(0, 1)*2)+1)% 4; lastdir = (seconddir + 2)%4; } seconddircheck = trymovetron(sim,x,y,seconddir,i,parts[i].tmp2); @@ -192,8 +192,8 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - int randhue = RNG::Ref().between(0, 359); - int randomdir = RNG::Ref().between(0, 3); + int randhue = sim->rng.between(0, 359); + int randomdir = sim->rng.between(0, 3); // Set as a head and a direction sim->parts[i].tmp = 1 | (randomdir << 5) | (randhue << 7); // Tail diff --git a/src/simulation/elements/TSNS.cpp b/src/simulation/elements/TSNS.cpp index a554d6501..4f84ed540 100644 --- a/src/simulation/elements/TSNS.cpp +++ b/src/simulation/elements/TSNS.cpp @@ -6,7 +6,7 @@ void Element::Element_TSNS() { Identifier = "DEFAULT_PT_TSNS"; Name = "TSNS"; - Colour = PIXPACK(0xFD00D5); + Colour = 0xFD00D5_rgb; MenuVisible = 1; MenuSection = SC_SENSOR; Enabled = 1; diff --git a/src/simulation/elements/TTAN.cpp b/src/simulation/elements/TTAN.cpp index 0b9c46a00..8fe4e4159 100644 --- a/src/simulation/elements/TTAN.cpp +++ b/src/simulation/elements/TTAN.cpp @@ -7,7 +7,7 @@ void Element::Element_TTAN() { Identifier = "DEFAULT_PT_TTAN"; Name = "TTAN"; - Colour = PIXPACK(0x909090); + Colour = 0x909090_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/TUNG.cpp b/src/simulation/elements/TUNG.cpp index 08cf3bc62..29317aaa0 100644 --- a/src/simulation/elements/TUNG.cpp +++ b/src/simulation/elements/TUNG.cpp @@ -9,7 +9,7 @@ void Element::Element_TUNG() { Identifier = "DEFAULT_PT_TUNG"; Name = "TUNG"; - Colour = PIXPACK(0x505050); + Colour = 0x505050_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; @@ -69,16 +69,16 @@ static int update(UPDATE_FUNC_ARGS) } } } - if((parts[i].temp > MELTING_POINT && RNG::Ref().chance(1, 20)) || splode) + if((parts[i].temp > MELTING_POINT && sim->rng.chance(1, 20)) || splode) { - if (RNG::Ref().chance(1, 50)) + if (sim->rng.chance(1, 50)) { sim->pv[y/CELL][x/CELL] += 50.0f; } - else if (RNG::Ref().chance(1, 100)) + else if (sim->rng.chance(1, 100)) { sim->part_change_type(i, x, y, PT_FIRE); - parts[i].life = RNG::Ref().between(0, 499); + parts[i].life = sim->rng.between(0, 499); return 1; } else @@ -89,10 +89,10 @@ static int update(UPDATE_FUNC_ARGS) } if(splode) { - parts[i].temp = restrict_flt(MELTING_POINT + RNG::Ref().between(200, 799), MIN_TEMP, MAX_TEMP); + parts[i].temp = restrict_flt(MELTING_POINT + sim->rng.between(200, 799), MIN_TEMP, MAX_TEMP); } - parts[i].vx += RNG::Ref().between(-50, 50); - parts[i].vy += RNG::Ref().between(-50, 50); + parts[i].vx += sim->rng.between(-50, 50); + parts[i].vy += sim->rng.between(-50, 50); return 1; } auto press = int(sim->pv[y/CELL][x/CELL] * 64); @@ -111,11 +111,11 @@ static int graphics(GRAPHICS_FUNC_ARGS) { const float MELTING_POINT = ren->sim->elements[PT_TUNG].HighTemperature; double startTemp = (MELTING_POINT - 1500.0); - double tempOver = (((cpart->temp - startTemp)/1500.0)*M_PI) - (M_PI/2.0); - if(tempOver > -(M_PI/2.0)) + double tempOver = (((cpart->temp - startTemp)/1500.0)*TPT_PI_FLT) - (TPT_PI_FLT/2.0); + if(tempOver > -(TPT_PI_FLT/2.0)) { - if(tempOver > (M_PI/2.0)) - tempOver = (M_PI/2.0); + if(tempOver > (TPT_PI_FLT/2.0)) + tempOver = (TPT_PI_FLT/2.0); double gradv = sin(tempOver) + 1.0; *firer = (int)(gradv * 258.0); *fireg = (int)(gradv * 156.0); diff --git a/src/simulation/elements/URAN.cpp b/src/simulation/elements/URAN.cpp index 4201d0408..dc7eab0cf 100644 --- a/src/simulation/elements/URAN.cpp +++ b/src/simulation/elements/URAN.cpp @@ -6,7 +6,7 @@ void Element::Element_URAN() { Identifier = "DEFAULT_PT_URAN"; Name = "URAN"; - Colour = PIXPACK(0x707020); + Colour = 0x707020_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; diff --git a/src/simulation/elements/VIBR.cpp b/src/simulation/elements/VIBR.cpp index 7862016e9..083352446 100644 --- a/src/simulation/elements/VIBR.cpp +++ b/src/simulation/elements/VIBR.cpp @@ -7,7 +7,7 @@ void Element::Element_VIBR() { Identifier = "DEFAULT_PT_VIBR"; Name = "VIBR"; - Colour = PIXPACK(0x005000); + Colour = 0x005000_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -83,7 +83,7 @@ int Element_VIBR_update(UPDATE_FUNC_ARGS) else //if it is exploding { //Release sparks before explode - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); if (parts[i].life < 300) { rx = rndstore%3-1; @@ -117,7 +117,7 @@ int Element_VIBR_update(UPDATE_FUNC_ARGS) { if (!parts[i].tmp2) { - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); int index = sim->create_part(-3,x+((rndstore>>4)&3)-1,y+((rndstore>>6)&3)-1,PT_ELEC); if (index != -1) parts[index].temp = 7000; @@ -125,7 +125,7 @@ int Element_VIBR_update(UPDATE_FUNC_ARGS) if (index != -1) parts[index].temp = 7000; int rx = ((rndstore>>12)&3)-1; - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); index = sim->create_part(-1,x+rx-1,y+rndstore%3-1,PT_BREC); if (index != -1) parts[index].temp = 7000; @@ -159,7 +159,7 @@ int Element_VIBR_update(UPDATE_FUNC_ARGS) { if (!parts[ID(r)].life) parts[ID(r)].tmp += 45; - else if (parts[i].tmp2 && parts[i].life > 75 && RNG::Ref().chance(1, 2)) + else if (parts[i].tmp2 && parts[i].life > 75 && sim->rng.chance(1, 2)) { parts[ID(r)].tmp2 = 1; parts[i].tmp = 0; @@ -174,7 +174,7 @@ int Element_VIBR_update(UPDATE_FUNC_ARGS) else { //Melts into EXOT - if (TYP(r) == PT_EXOT && RNG::Ref().chance(1, 25)) + if (TYP(r) == PT_EXOT && sim->rng.chance(1, 25)) { sim->part_change_type(i, x, y, PT_EXOT); return 1; @@ -190,7 +190,7 @@ int Element_VIBR_update(UPDATE_FUNC_ARGS) for (trade = 0; trade < 9; trade++) { if (!(trade%2)) - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); rx = rndstore%7-3; rndstore >>= 3; ry = rndstore%7-3; diff --git a/src/simulation/elements/VINE.cpp b/src/simulation/elements/VINE.cpp index 566d7c1de..68048c14a 100644 --- a/src/simulation/elements/VINE.cpp +++ b/src/simulation/elements/VINE.cpp @@ -8,7 +8,7 @@ void Element::Element_VINE() { Identifier = "DEFAULT_PT_VINE"; Name = "VINE"; - Colour = PIXPACK(0x079A00); + Colour = 0x079A00_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; @@ -52,7 +52,7 @@ void Element::Element_VINE() static int update(UPDATE_FUNC_ARGS) { - int r, np, rx, ry, rndstore = RNG::Ref().gen(); + int r, np, rx, ry, rndstore = sim->rng.gen(); rx = (rndstore % 3) - 1; rndstore >>= 2; ry = (rndstore % 3) - 1; diff --git a/src/simulation/elements/VIRS.cpp b/src/simulation/elements/VIRS.cpp index 505674c72..4c01f0838 100644 --- a/src/simulation/elements/VIRS.cpp +++ b/src/simulation/elements/VIRS.cpp @@ -7,7 +7,7 @@ void Element::Element_VIRS() { Identifier = "DEFAULT_PT_VIRS"; Name = "VIRS"; - Colour = PIXPACK(0xFE11F6); + Colour = 0xFE11F6_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -34,6 +34,7 @@ void Element::Element_VIRS() Description = "Virus. Turns everything it touches into virus."; Properties = TYPE_LIQUID|PROP_DEADLY; + CarriesTypeIn = 1U << FIELD_TMP2; LowPressure = IPL; LowPressureTransition = NT; @@ -54,7 +55,7 @@ int Element_VIRS_update(UPDATE_FUNC_ARGS) { //tmp3 measures how many frames until it is cured (0 if still actively spreading and not being cured) //tmp4 measures how many frames until it dies - int r, rx, ry, rndstore = RNG::Ref().gen(); + int r, rx, ry, rndstore = sim->rng.gen(); if (parts[i].tmp3) { parts[i].tmp3 -= (rndstore & 0x1) ? 0:1; @@ -105,7 +106,7 @@ int Element_VIRS_update(UPDATE_FUNC_ARGS) } else if (TYP(r) == PT_PLSM) { - if (surround_space && RNG::Ref().chance(10 + int(sim->pv[(y+ry)/CELL][(x+rx)/CELL]), 100)) + if (surround_space && sim->rng.chance(10 + int(sim->pv[(y+ry)/CELL][(x+rx)/CELL]), 100)) { sim->create_part(i, x, y, PT_PLSM); return 1; @@ -139,7 +140,7 @@ int Element_VIRS_update(UPDATE_FUNC_ARGS) } //reset rndstore only once, halfway through else if (!rx && !ry) - rndstore = RNG::Ref().gen(); + rndstore = sim->rng.gen(); } return 0; } diff --git a/src/simulation/elements/VOID.cpp b/src/simulation/elements/VOID.cpp index 39b9d9a64..fc64670c5 100644 --- a/src/simulation/elements/VOID.cpp +++ b/src/simulation/elements/VOID.cpp @@ -4,7 +4,7 @@ void Element::Element_VOID() { Identifier = "DEFAULT_PT_VOID"; Name = "VOID"; - Colour = PIXPACK(0x790B0B); + Colour = 0x790B0B_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; diff --git a/src/simulation/elements/VRSG.cpp b/src/simulation/elements/VRSG.cpp index c9a8f45c4..11a204af2 100644 --- a/src/simulation/elements/VRSG.cpp +++ b/src/simulation/elements/VRSG.cpp @@ -7,7 +7,7 @@ void Element::Element_VRSG() { Identifier = "DEFAULT_PT_VRSG"; Name = "VRSG"; - Colour = PIXPACK(0xFE68FE); + Colour = 0xFE68FE_rgb; MenuVisible = 0; MenuSection = SC_GAS; Enabled = 1; @@ -34,6 +34,7 @@ void Element::Element_VRSG() Description = "Gas Virus. Turns everything it touches into virus."; Properties = TYPE_GAS|PROP_DEADLY; + CarriesTypeIn = 1U << FIELD_TMP2; LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/VRSS.cpp b/src/simulation/elements/VRSS.cpp index 3c849f562..6ba0fee9f 100644 --- a/src/simulation/elements/VRSS.cpp +++ b/src/simulation/elements/VRSS.cpp @@ -7,7 +7,7 @@ void Element::Element_VRSS() { Identifier = "DEFAULT_PT_VRSS"; Name = "VRSS"; - Colour = PIXPACK(0xD408CD); + Colour = 0xD408CD_rgb; MenuVisible = 0; MenuSection = SC_SOLIDS; Enabled = 1; @@ -34,6 +34,7 @@ void Element::Element_VRSS() Description = "Solid Virus. Turns everything it touches into virus."; Properties = TYPE_SOLID|PROP_DEADLY; + CarriesTypeIn = 1U << FIELD_TMP2; LowPressure = IPL; LowPressureTransition = NT; diff --git a/src/simulation/elements/VSNS.cpp b/src/simulation/elements/VSNS.cpp index a5e8693a0..f4b8eccd5 100644 --- a/src/simulation/elements/VSNS.cpp +++ b/src/simulation/elements/VSNS.cpp @@ -6,7 +6,7 @@ void Element::Element_VSNS() { Identifier = "DEFAULT_PT_VSNS"; Name = "VSNS"; - Colour = PIXPACK(0x7C9C00); + Colour = 0x7C9C00_rgb; MenuVisible = 1; MenuSection = SC_SENSOR; Enabled = 1; diff --git a/src/simulation/elements/WARP.cpp b/src/simulation/elements/WARP.cpp index cc6737868..311566ce7 100644 --- a/src/simulation/elements/WARP.cpp +++ b/src/simulation/elements/WARP.cpp @@ -8,7 +8,7 @@ void Element::Element_WARP() { Identifier = "DEFAULT_PT_WARP"; Name = "WARP"; - Colour = PIXPACK(0x101010); + Colour = 0x101010_rgb; MenuVisible = 1; MenuSection = SC_NUCLEAR; Enabled = 1; @@ -55,13 +55,13 @@ static int update(UPDATE_FUNC_ARGS) { parts[i].temp = 10000; sim->pv[y/CELL][x/CELL] += (parts[i].tmp2 / 5000) * CFDS; - if (RNG::Ref().chance(1, 50)) + if (sim->rng.chance(1, 50)) sim->create_part(-3, x, y, PT_ELEC); } for (int trade = 0; trade < 5; trade ++) { - int rx = RNG::Ref().between(-1, 1); - int ry = RNG::Ref().between(-1, 1); + int rx = sim->rng.between(-1, 1); + int ry = sim->rng.between(-1, 1); if (BOUNDS_CHECK && (rx || ry)) { int r = pmap[y + ry][x + rx]; @@ -73,8 +73,8 @@ static int update(UPDATE_FUNC_ARGS) parts[i].y = parts[ID(r)].y; parts[ID(r)].x = float(x); parts[ID(r)].y = float(y); - parts[ID(r)].vx = RNG::Ref().between(-2, 1) + 0.5f; - parts[ID(r)].vy = float(RNG::Ref().between(-2, 1)); + parts[ID(r)].vx = sim->rng.between(-2, 1) + 0.5f; + parts[ID(r)].vy = float(sim->rng.between(-2, 1)); parts[i].life += 4; pmap[y][x] = r; pmap[y + ry][x + rx] = PMAP(i, parts[i].type); @@ -94,5 +94,5 @@ static int graphics(GRAPHICS_FUNC_ARGS) static void create(ELEMENT_CREATE_FUNC_ARGS) { - sim->parts[i].life = RNG::Ref().between(70, 164); + sim->parts[i].life = sim->rng.between(70, 164); } diff --git a/src/simulation/elements/WATR.cpp b/src/simulation/elements/WATR.cpp index 3adcc2a80..1ed233189 100644 --- a/src/simulation/elements/WATR.cpp +++ b/src/simulation/elements/WATR.cpp @@ -6,7 +6,7 @@ void Element::Element_WATR() { Identifier = "DEFAULT_PT_WATR"; Name = "WATR"; - Colour = PIXPACK(0x2030D0); + Colour = 0x2030D0_rgb; MenuVisible = 1; MenuSection = SC_LIQUID; Enabled = 1; @@ -56,14 +56,14 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)==PT_SALT && RNG::Ref().chance(1, 50)) + if (TYP(r)==PT_SALT && sim->rng.chance(1, 50)) { sim->part_change_type(i,x,y,PT_SLTW); // on average, convert 3 WATR to SLTW before SALT turns into SLTW - if (RNG::Ref().chance(1, 3)) + if (sim->rng.chance(1, 3)) sim->part_change_type(ID(r),x+rx,y+ry,PT_SLTW); } - else if ((TYP(r)==PT_RBDM||TYP(r)==PT_LRBD) && (sim->legacy_enable||parts[i].temp>(273.15f+12.0f)) && RNG::Ref().chance(1, 100)) + else if ((TYP(r)==PT_RBDM||TYP(r)==PT_LRBD) && (sim->legacy_enable||parts[i].temp>(273.15f+12.0f)) && sim->rng.chance(1, 100)) { sim->part_change_type(i,x,y,PT_FIRE); parts[i].life = 4; @@ -72,19 +72,19 @@ static int update(UPDATE_FUNC_ARGS) else if (TYP(r)==PT_FIRE && parts[ID(r)].ctype!=PT_WATR) { sim->kill_part(ID(r)); - if (RNG::Ref().chance(1, 30)) + if (sim->rng.chance(1, 30)) { sim->kill_part(i); return 1; } } - else if (TYP(r)==PT_SLTW && RNG::Ref().chance(1, 2000)) + else if (TYP(r)==PT_SLTW && sim->rng.chance(1, 2000)) { sim->part_change_type(i,x,y,PT_SLTW); } - else if (TYP(r)==PT_ROCK && fabs(parts[i].vx)+fabs(parts[i].vy) >= 0.5 && RNG::Ref().chance(1, 1000)) // ROCK erosion + else if (TYP(r)==PT_ROCK && fabs(parts[i].vx)+fabs(parts[i].vy) >= 0.5 && sim->rng.chance(1, 1000)) // ROCK erosion { - if (RNG::Ref().chance(1,3)) + if (sim->rng.chance(1,3)) sim->part_change_type(ID(r),x+rx,y+ry,PT_SAND); else sim->part_change_type(ID(r),x+rx,y+ry,PT_STNE); diff --git a/src/simulation/elements/WAX.cpp b/src/simulation/elements/WAX.cpp index ba5ab7c5c..95a345758 100644 --- a/src/simulation/elements/WAX.cpp +++ b/src/simulation/elements/WAX.cpp @@ -4,7 +4,7 @@ void Element::Element_WAX() { Identifier = "DEFAULT_PT_WAX"; Name = "WAX"; - Colour = PIXPACK(0xF0F0BB); + Colour = 0xF0F0BB_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/WHOL.cpp b/src/simulation/elements/WHOL.cpp index bbf6f159e..1b775fa20 100644 --- a/src/simulation/elements/WHOL.cpp +++ b/src/simulation/elements/WHOL.cpp @@ -4,7 +4,7 @@ void Element::Element_WHOL() { Identifier = "DEFAULT_PT_WHOL"; Name = "VENT"; - Colour = PIXPACK(0xEFEFEF); + Colour = 0xEFEFEF_rgb; MenuVisible = 1; MenuSection = SC_SPECIAL; Enabled = 1; diff --git a/src/simulation/elements/WIFI.cpp b/src/simulation/elements/WIFI.cpp index 4a4b1f49e..55f692a46 100644 --- a/src/simulation/elements/WIFI.cpp +++ b/src/simulation/elements/WIFI.cpp @@ -7,7 +7,7 @@ void Element::Element_WIFI() { Identifier = "DEFAULT_PT_WIFI"; Name = "WIFI"; - Colour = PIXPACK(0x40A060); + Colour = 0x40A060_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/WIRE.cpp b/src/simulation/elements/WIRE.cpp index 47ee8bf11..4d2653150 100644 --- a/src/simulation/elements/WIRE.cpp +++ b/src/simulation/elements/WIRE.cpp @@ -7,7 +7,7 @@ void Element::Element_WIRE() { Identifier = "DEFAULT_PT_WIRE"; Name = "WWLD"; - Colour = PIXPACK(0xFFCC00); + Colour = 0xFFCC00_rgb; MenuVisible = 1; MenuSection = SC_ELEC; Enabled = 1; diff --git a/src/simulation/elements/WOOD.cpp b/src/simulation/elements/WOOD.cpp index 8bcbd3e97..45f8231cc 100644 --- a/src/simulation/elements/WOOD.cpp +++ b/src/simulation/elements/WOOD.cpp @@ -8,7 +8,7 @@ void Element::Element_WOOD() { Identifier = "DEFAULT_PT_WOOD"; Name = "WOOD"; - Colour = PIXPACK(0xC0A040); + Colour = 0xC0A040_rgb; MenuVisible = 1; MenuSection = SC_SOLIDS; Enabled = 1; diff --git a/src/simulation/elements/WTRV.cpp b/src/simulation/elements/WTRV.cpp index 3ea937082..94df68002 100644 --- a/src/simulation/elements/WTRV.cpp +++ b/src/simulation/elements/WTRV.cpp @@ -6,7 +6,7 @@ void Element::Element_WTRV() { Identifier = "DEFAULT_PT_WTRV"; Name = "WTRV"; - Colour = PIXPACK(0xA0A0FF); + Colour = 0xA0A0FF_rgb; MenuVisible = 1; MenuSection = SC_GAS; Enabled = 1; @@ -56,7 +56,7 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if ((TYP(r)==PT_RBDM||TYP(r)==PT_LRBD) && !sim->legacy_enable && parts[i].temp>(273.15f+12.0f) && RNG::Ref().chance(1, 100)) + if ((TYP(r)==PT_RBDM||TYP(r)==PT_LRBD) && !sim->legacy_enable && parts[i].temp>(273.15f+12.0f) && sim->rng.chance(1, 100)) { sim->part_change_type(i,x,y,PT_FIRE); parts[i].life = 4; diff --git a/src/simulation/elements/YEST.cpp b/src/simulation/elements/YEST.cpp index 772b90175..c6bf2e53a 100644 --- a/src/simulation/elements/YEST.cpp +++ b/src/simulation/elements/YEST.cpp @@ -6,7 +6,7 @@ void Element::Element_YEST() { Identifier = "DEFAULT_PT_YEST"; Name = "YEST"; - Colour = PIXPACK(0xEEE0C0); + Colour = 0xEEE0C0_rgb; MenuVisible = 1; MenuSection = SC_POWDERS; Enabled = 1; @@ -55,13 +55,13 @@ static int update(UPDATE_FUNC_ARGS) r = pmap[y+ry][x+rx]; if (!r) continue; - if (TYP(r)==PT_DYST && RNG::Ref().chance(1, 6) && !sim->legacy_enable) + if (TYP(r)==PT_DYST && sim->rng.chance(1, 6) && !sim->legacy_enable) { sim->part_change_type(i,x,y,PT_DYST); } } if (parts[i].temp > 303 && parts[i].temp < 317) { - sim->create_part(-1, x + RNG::Ref().between(-1, 1), y + RNG::Ref().between(-1, 1), PT_YEST); + sim->create_part(-1, x + sim->rng.between(-1, 1), y + sim->rng.between(-1, 1), PT_YEST); } return 0; } diff --git a/src/simulation/gravity/Common.cpp b/src/simulation/gravity/Common.cpp new file mode 100644 index 000000000..d73f12783 --- /dev/null +++ b/src/simulation/gravity/Common.cpp @@ -0,0 +1,287 @@ +#include "Gravity.h" +#include "simulation/CoordStack.h" +#include "simulation/Simulation.h" +#include "simulation/SimulationData.h" +#include "Misc.h" +#include +#include +#include + +Gravity::Gravity(CtorTag) +{ + th_ogravmap.resize(NCELL); + th_gravmap.resize(NCELL); + th_gravy.resize(NCELL); + th_gravx.resize(NCELL); + th_gravp.resize(NCELL); + gravmap.resize(NCELL); + gravy.resize(NCELL); + gravx.resize(NCELL); + gravp.resize(NCELL); + gravmask.resize(NCELL); +} + +Gravity::~Gravity() +{ + stop_grav_async(); +} + +void Gravity::Clear() +{ + std::fill(&gravy[0], &gravy[0] + NCELL, 0.0f); + std::fill(&gravx[0], &gravx[0] + NCELL, 0.0f); + std::fill(&gravp[0], &gravp[0] + NCELL, 0.0f); + std::fill(&gravmap[0], &gravmap[0] + NCELL, 0.0f); + std::fill(&gravmask[0], &gravmask[0] + NCELL, UINT32_C(0xFFFFFFFF)); + + ignoreNextResult = true; +} + +void Gravity::gravity_update_async() +{ + int result; + if (!enabled) + return; + + bool signal_grav = false; + + { + std::unique_lock l(gravmutex, std::defer_lock); + if (l.try_lock()) + { + result = grav_ready; + if (result) //Did the gravity thread finish? + { + if (th_gravchanged && !ignoreNextResult) + { + // Copy thread gravity maps into this one + get_result(); + } + ignoreNextResult = false; + + std::swap(gravmap, th_gravmap); + + grav_ready = 0; //Tell the other thread that we're ready for it to continue + signal_grav = true; + } + } + } + + if (signal_grav) + { + gravcv.notify_one(); + } + unsigned int size = NCELL; + membwand(&gravy[0], &gravmask[0], size * sizeof(float), size * sizeof(uint32_t)); + membwand(&gravx[0], &gravmask[0], size * sizeof(float), size * sizeof(uint32_t)); + std::fill(&gravmap[0], &gravmap[0] + size, 0.0f); +} + +void Gravity::update_grav_async() +{ + int done = 0; + int thread_done = 0; + std::fill(&th_ogravmap[0], &th_ogravmap[0] + NCELL, 0.0f); + std::fill(&th_gravmap[0], &th_gravmap[0] + NCELL, 0.0f); + std::fill(&th_gravy[0], &th_gravy[0] + NCELL, 0.0f); + std::fill(&th_gravx[0], &th_gravx[0] + NCELL, 0.0f); + std::fill(&th_gravp[0], &th_gravp[0] + NCELL, 0.0f); + + std::unique_lock l(gravmutex); + while (!thread_done) + { + if (!done) + { + // run gravity update + update_grav(); + done = 1; + grav_ready = 1; + thread_done = gravthread_done; + } + else + { + // wait for main thread + gravcv.wait(l); + done = grav_ready; + thread_done = gravthread_done; + } + } +} + +void Gravity::start_grav_async() +{ + if (enabled) //If it's already enabled, restart it + stop_grav_async(); + + gravthread_done = 0; + grav_ready = 0; + gravthread = std::thread([this]() { update_grav_async(); }); //Start asynchronous gravity simulation + enabled = true; + + std::fill(&gravy[0], &gravy[0] + NCELL, 0.0f); + std::fill(&gravx[0], &gravx[0] + NCELL, 0.0f); + std::fill(&gravp[0], &gravp[0] + NCELL, 0.0f); + std::fill(&gravmap[0], &gravmap[0] + NCELL, 0.0f); +} + +void Gravity::stop_grav_async() +{ + if (enabled) + { + { + std::lock_guard g(gravmutex); + gravthread_done = 1; + } + gravcv.notify_one(); + gravthread.join(); + enabled = false; + } + // Clear the grav velocities + std::fill(&gravy[0], &gravy[0] + NCELL, 0.0f); + std::fill(&gravx[0], &gravx[0] + NCELL, 0.0f); + std::fill(&gravp[0], &gravp[0] + NCELL, 0.0f); + std::fill(&gravmap[0], &gravmap[0] + NCELL, 0.0f); +} + +bool Gravity::grav_mask_r(int x, int y, char checkmap[YCELLS][XCELLS], char shape[YCELLS][XCELLS]) +{ + int x1, x2; + bool ret = false; + try + { + CoordStack cs; + cs.push(x, y); + do + { + cs.pop(x, y); + x1 = x2 = x; + while (x1 >= 0) + { + if (x1 == 0) + { + ret = true; + break; + } + else if (checkmap[y][x1-1] || bmap[y][x1-1] == WL_GRAV) + break; + x1--; + } + while (x2 <= XCELLS-1) + { + if (x2 == XCELLS-1) + { + ret = true; + break; + } + else if (checkmap[y][x2+1] || bmap[y][x2+1] == WL_GRAV) + break; + x2++; + } + for (x = x1; x <= x2; x++) + { + shape[y][x] = 1; + checkmap[y][x] = 1; + } + if (y == 0) + { + for (x = x1; x <= x2; x++) + if (bmap[y][x] != WL_GRAV) + ret = true; + } + else if (y >= 1) + { + for (x = x1; x <= x2; x++) + if (!checkmap[y-1][x] && bmap[y-1][x] != WL_GRAV) + { + if (y-1 == 0) + ret = true; + cs.push(x, y-1); + } + } + if (y < YCELLS-1) + for (x=x1; x<=x2; x++) + if (!checkmap[y+1][x] && bmap[y+1][x] != WL_GRAV) + { + if (y+1 == YCELLS-1) + ret = true; + cs.push(x, y+1); + } + } while (cs.getSize()>0); + } + catch (std::exception& e) + { + std::cerr << e.what() << std::endl; + ret = false; + } + return ret; +} +void Gravity::mask_free(mask_el *c_mask_el) +{ + if (c_mask_el == nullptr) + return; + delete[] c_mask_el->next; + delete[] c_mask_el->shape; + delete[] c_mask_el; +} + +void Gravity::gravity_mask() +{ + char checkmap[YCELLS][XCELLS]; + unsigned maskvalue; + mask_el *t_mask_el = nullptr; + mask_el *c_mask_el = nullptr; + memset(checkmap, 0, sizeof(checkmap)); + for (int x = 0; x < XCELLS; x++) + { + for(int y = 0; y < YCELLS; y++) + { + if (bmap[y][x] != WL_GRAV && checkmap[y][x] == 0) + { + // Create a new shape + if (t_mask_el == nullptr) + { + t_mask_el = new mask_el[sizeof(mask_el)]; + t_mask_el->shape = new char[NCELL]; + std::fill(&t_mask_el->shape[0], &t_mask_el->shape[0] + NCELL, 0); + t_mask_el->shapeout = 0; + t_mask_el->next = nullptr; + c_mask_el = t_mask_el; + } + else + { + c_mask_el->next = new mask_el[sizeof(mask_el)]; + c_mask_el = c_mask_el->next; + c_mask_el->shape = new char[NCELL]; + std::fill(&c_mask_el->shape[0], &c_mask_el->shape[0] + NCELL, 0); + c_mask_el->shapeout = 0; + c_mask_el->next = nullptr; + } + // Fill the shape + if (grav_mask_r(x, y, checkmap, reinterpret_cast(c_mask_el->shape))) + c_mask_el->shapeout = 1; + } + } + } + c_mask_el = t_mask_el; + std::fill(&gravmask[0], &gravmask[0] + NCELL, 0); + while (c_mask_el != nullptr) + { + char *cshape = c_mask_el->shape; + for (int x = 0; x < XCELLS; x++) + { + for (int y = 0; y < YCELLS; y++) + { + if (cshape[y * XCELLS + x]) + { + if (c_mask_el->shapeout) + maskvalue = 0xFFFFFFFF; + else + maskvalue = 0x00000000; + gravmask[y * XCELLS + x] = maskvalue; + } + } + } + c_mask_el = c_mask_el->next; + } + mask_free(t_mask_el); +} diff --git a/src/simulation/gravity/Fft.cpp b/src/simulation/gravity/Fft.cpp new file mode 100644 index 000000000..4d8cac5f8 --- /dev/null +++ b/src/simulation/gravity/Fft.cpp @@ -0,0 +1,184 @@ +#include "Gravity.h" +#include "Misc.h" +#include +#include +#include +#include + +constexpr auto xblock2 = XCELLS * 2; +constexpr auto yblock2 = YCELLS * 2; +constexpr auto fft_tsize = (xblock2 / 2 + 1) * yblock2; +//NCELL*4 is size of data array, scaling needed because FFTW calculates an unnormalized DFT +constexpr auto scaleFactor = -float(M_GRAV) / (NCELL * 4); + +static_assert(sizeof(std::complex) == sizeof(fftwf_complex)); +struct FftwArrayDeleter { void operator ()(float ptr[]) const { fftwf_free(ptr); } }; +struct FftwComplexArrayDeleter { void operator ()(std::complex ptr[]) const { fftwf_free(ptr); } }; +struct FftwPlanDeleter { void operator ()(fftwf_plan ptr ) const { fftwf_destroy_plan(ptr); } }; +using FftwArrayPtr = std::unique_ptr; +using FftwComplexArrayPtr = std::unique_ptr [], FftwComplexArrayDeleter>; +using FftwPlanPtr = std::unique_ptr::type, FftwPlanDeleter >; +FftwArrayPtr FftwArray(size_t size) +{ + return FftwArrayPtr(reinterpret_cast(fftwf_malloc(size * sizeof(float)))); +} +FftwComplexArrayPtr FftwComplexArray(size_t size) +{ + return FftwComplexArrayPtr(reinterpret_cast *>(fftwf_malloc(size * sizeof(std::complex)))); +} + +struct GravityImpl : public Gravity +{ + bool grav_fft_status = false; + FftwArrayPtr th_gravmapbig , th_gravxbig , th_gravybig ; + FftwComplexArrayPtr th_ptgravxt, th_ptgravyt, th_gravmapbigt, th_gravxbigt, th_gravybigt; + FftwPlanPtr plan_gravmap, plan_gravx_inverse, plan_gravy_inverse; + + void grav_fft_init(); + void grav_fft_cleanup(); + + GravityImpl() : Gravity(CtorTag{}) + { + } + + ~GravityImpl(); +}; + +GravityImpl::~GravityImpl() +{ + stop_grav_async(); + grav_fft_cleanup(); +} + +void GravityImpl::grav_fft_init() +{ + if (grav_fft_status) return; + FftwPlanPtr plan_ptgravx, plan_ptgravy; + + //use fftw malloc function to ensure arrays are aligned, to get better performance + FftwArrayPtr th_ptgravx = FftwArray(xblock2 * yblock2); + FftwArrayPtr th_ptgravy = FftwArray(xblock2 * yblock2); + th_ptgravxt = FftwComplexArray(fft_tsize); + th_ptgravyt = FftwComplexArray(fft_tsize); + th_gravmapbig = FftwArray(xblock2 * yblock2); + th_gravmapbigt = FftwComplexArray(fft_tsize); + th_gravxbig = FftwArray(xblock2 * yblock2); + th_gravybig = FftwArray(xblock2 * yblock2); + th_gravxbigt = FftwComplexArray(fft_tsize); + th_gravybigt = FftwComplexArray(fft_tsize); + + //select best algorithm, could use FFTW_PATIENT or FFTW_EXHAUSTIVE but that increases the time taken to plan, and I don't see much increase in execution speed + plan_ptgravx = FftwPlanPtr(fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravx.get(), reinterpret_cast(th_ptgravxt.get()), FFTW_MEASURE)); + plan_ptgravy = FftwPlanPtr(fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_ptgravy.get(), reinterpret_cast(th_ptgravyt.get()), FFTW_MEASURE)); + plan_gravmap = FftwPlanPtr(fftwf_plan_dft_r2c_2d(yblock2, xblock2, th_gravmapbig.get(), reinterpret_cast(th_gravmapbigt.get()), FFTW_MEASURE)); + plan_gravx_inverse = FftwPlanPtr(fftwf_plan_dft_c2r_2d(yblock2, xblock2, reinterpret_cast(th_gravxbigt.get()), th_gravxbig.get(), FFTW_MEASURE)); + plan_gravy_inverse = FftwPlanPtr(fftwf_plan_dft_c2r_2d(yblock2, xblock2, reinterpret_cast(th_gravybigt.get()), th_gravybig.get(), FFTW_MEASURE)); + + //calculate velocity map caused by a point mass + for (int y = 0; y < yblock2; y++) + { + for (int x = 0; x < xblock2; x++) + { + if (x == XCELLS && y == YCELLS) + continue; + auto distance = hypotf(float(x-XCELLS), float(y-YCELLS)); + th_ptgravx[y * xblock2 + x] = scaleFactor * (x - XCELLS) / powf(distance, 3); + th_ptgravy[y * xblock2 + x] = scaleFactor * (y - YCELLS) / powf(distance, 3); + } + } + th_ptgravx[yblock2 * xblock2 / 2 + xblock2 / 2] = 0.0f; + th_ptgravy[yblock2 * xblock2 / 2 + xblock2 / 2] = 0.0f; + + //transform point mass velocity maps + fftwf_execute(plan_ptgravx.get()); + fftwf_execute(plan_ptgravy.get()); + + //clear padded gravmap + memset(th_gravmapbig.get(), 0, xblock2 * yblock2 * sizeof(float)); + + grav_fft_status = true; +} + +void GravityImpl::grav_fft_cleanup() +{ + if (!grav_fft_status) return; + grav_fft_status = false; +} + +void Gravity::get_result() +{ + std::swap(gravy, th_gravy); + std::swap(gravx, th_gravx); + std::swap(gravp, th_gravp); +} + +void Gravity::update_grav() +{ + auto *fftGravity = static_cast(this); + if (!fftGravity->grav_fft_status) + fftGravity->grav_fft_init(); + + auto *th_gravmapbig = fftGravity->th_gravmapbig.get(); + auto *th_gravxbig = fftGravity->th_gravxbig.get(); + auto *th_gravybig = fftGravity->th_gravybig.get(); + auto *th_ptgravxt = fftGravity->th_ptgravxt.get(); + auto *th_ptgravyt = fftGravity->th_ptgravyt.get(); + auto *th_gravmapbigt = fftGravity->th_gravmapbigt.get(); + auto *th_gravxbigt = fftGravity->th_gravxbigt.get(); + auto *th_gravybigt = fftGravity->th_gravybigt.get(); + auto &plan_gravmap = fftGravity->plan_gravmap; + auto &plan_gravx_inverse = fftGravity->plan_gravx_inverse; + auto &plan_gravy_inverse = fftGravity->plan_gravy_inverse; + + if (memcmp(&th_ogravmap[0], &th_gravmap[0], sizeof(float) * NCELL) != 0) + { + th_gravchanged = 1; + + membwand(&th_gravmap[0], &gravmask[0], NCELL * sizeof(float), NCELL * sizeof(uint32_t)); + //copy gravmap into padded gravmap array + for (int y = 0; y < YCELLS; y++) + { + for (int x = 0; x < XCELLS; x++) + { + th_gravmapbig[(y+YCELLS)*xblock2+XCELLS+x] = th_gravmap[y*XCELLS+x]; + } + } + //transform gravmap + fftwf_execute(plan_gravmap.get()); + //do convolution (multiply the complex numbers) + for (int i = 0; i < fft_tsize; i++) + { + th_gravxbigt[i] = th_gravmapbigt[i] * th_ptgravxt[i]; + th_gravybigt[i] = th_gravmapbigt[i] * th_ptgravyt[i]; + } + //inverse transform, and copy from padded arrays into normal velocity maps + fftwf_execute(plan_gravx_inverse.get()); + fftwf_execute(plan_gravy_inverse.get()); + for (int y = 0; y < YCELLS; y++) + { + for (int x = 0; x < XCELLS; x++) + { + th_gravx[y*XCELLS+x] = th_gravxbig[y*xblock2+x]; + th_gravy[y*XCELLS+x] = th_gravybig[y*xblock2+x]; + th_gravp[y*XCELLS+x] = hypotf(th_gravxbig[y*xblock2+x], th_gravybig[y*xblock2+x]); + } + } + } + else + { + th_gravchanged = 0; + } + + // Copy th_ogravmap into th_gravmap (doesn't matter what th_ogravmap is afterwards) + std::swap(th_gravmap, th_ogravmap); +} + +GravityPtr Gravity::Create() +{ + return GravityPtr(new GravityImpl()); +} + +void GravityDeleter::operator ()(Gravity *ptr) const +{ + delete static_cast(ptr); +} diff --git a/src/simulation/gravity/Gravity.h b/src/simulation/gravity/Gravity.h new file mode 100644 index 000000000..bec94ab68 --- /dev/null +++ b/src/simulation/gravity/Gravity.h @@ -0,0 +1,78 @@ +#pragma once +#include "Config.h" +#include "GravityPtr.h" +#include "SimulationConfig.h" +#include +#include +#include +#include +#include +#include + +class Simulation; + +class Gravity +{ +protected: + bool enabled = false; + + // Maps to be processed by the gravity thread + std::vector th_ogravmap; + std::vector th_gravmap; + std::vector th_gravx; + std::vector th_gravy; + std::vector th_gravp; + + int th_gravchanged = 0; + + std::thread gravthread; + std::mutex gravmutex; + std::condition_variable gravcv; + int grav_ready = 0; + int gravthread_done = 0; + bool ignoreNextResult = false; + + struct mask_el { + char *shape; + char shapeout; + mask_el *next; + }; + using mask_el = struct mask_el; + + bool grav_mask_r(int x, int y, char checkmap[YCELLS][XCELLS], char shape[YCELLS][XCELLS]); + void mask_free(mask_el *c_mask_el); + + void update_grav(); + void get_result(); + void update_grav_async(); + + struct CtorTag // Please use Gravity::Create(). + { + }; + +public: + Gravity(CtorTag); + ~Gravity(); + + //Maps to be used by the main thread + std::vector gravmap; + std::vector gravp; + std::vector gravy; + std::vector gravx; + std::vector gravmask; + static_assert(sizeof(float) == sizeof(uint32_t)); + + unsigned char (*bmap)[XCELLS]; + + bool IsEnabled() { return enabled; } + + void Clear(); + + void gravity_update_async(); + + void start_grav_async(); + void stop_grav_async(); + void gravity_mask(); + + static GravityPtr Create(); +}; diff --git a/src/simulation/gravity/GravityPtr.h b/src/simulation/gravity/GravityPtr.h new file mode 100644 index 000000000..b58e096e3 --- /dev/null +++ b/src/simulation/gravity/GravityPtr.h @@ -0,0 +1,9 @@ +#pragma once +#include + +class Gravity; +struct GravityDeleter +{ + void operator ()(Gravity *ptr) const; +}; +using GravityPtr = std::unique_ptr; diff --git a/src/simulation/gravity/Null.cpp b/src/simulation/gravity/Null.cpp new file mode 100644 index 000000000..02571fff6 --- /dev/null +++ b/src/simulation/gravity/Null.cpp @@ -0,0 +1,26 @@ +#include "Gravity.h" +#include "Misc.h" +#include + +// gravity without fast Fourier transforms + +void Gravity::get_result() +{ + memcpy(&gravy[0], &th_gravy[0], NCELL * sizeof(float)); + memcpy(&gravx[0], &th_gravx[0], NCELL * sizeof(float)); + memcpy(&gravp[0], &th_gravp[0], NCELL * sizeof(float)); +} + +void Gravity::update_grav(void) +{ +} + +GravityPtr Gravity::Create() +{ + return GravityPtr(new Gravity(CtorTag{})); +} + +void GravityDeleter::operator ()(Gravity *ptr) const +{ + delete ptr; +} diff --git a/src/simulation/gravity/meson.build b/src/simulation/gravity/meson.build new file mode 100644 index 000000000..8a4b67e17 --- /dev/null +++ b/src/simulation/gravity/meson.build @@ -0,0 +1,6 @@ +simulation_files += files( + 'Common.cpp', +) + +powder_files += files('Fft.cpp') +render_files += files('Null.cpp') diff --git a/src/simulation/meson.build b/src/simulation/meson.build index adb49630d..76cb360d5 100644 --- a/src/simulation/meson.build +++ b/src/simulation/meson.build @@ -3,19 +3,27 @@ simulation_files = files( 'Element.cpp', 'ElementClasses.cpp', 'GOLString.cpp', - 'Gravity.cpp', 'Particle.cpp', 'SaveRenderer.cpp', 'Sign.cpp', - 'SimTool.cpp', 'SimulationData.cpp', - 'ToolClasses.cpp', 'Simulation.cpp', - 'SnapshotDelta.cpp', ) subdir('elements') subdir('simtools') +subdir('gravity') powder_files += simulation_files render_files += simulation_files + +powder_files += files( + 'Editing.cpp', + 'SimTool.cpp', + 'ToolClasses.cpp', + 'Snapshot.cpp', + 'SnapshotDelta.cpp', +) +render_files += files( + 'NoToolClasses.cpp', +) diff --git a/src/simulation/simtools/AIR.cpp b/src/simulation/simtools/AIR.cpp index 6d2538529..2115f6461 100644 --- a/src/simulation/simtools/AIR.cpp +++ b/src/simulation/simtools/AIR.cpp @@ -7,7 +7,7 @@ void SimTool::Tool_AIR() { Identifier = "DEFAULT_TOOL_AIR"; Name = "AIR"; - Colour = PIXPACK(0xFFFFFF); + Colour = 0xFFFFFF_rgb; Description = "Air, creates airflow and pressure."; Perform = &perform; } diff --git a/src/simulation/simtools/AMBM.cpp b/src/simulation/simtools/AMBM.cpp index 692ce36c8..4cf928079 100644 --- a/src/simulation/simtools/AMBM.cpp +++ b/src/simulation/simtools/AMBM.cpp @@ -6,7 +6,7 @@ void SimTool::Tool_AMBM() { Identifier = "DEFAULT_TOOL_AMBM"; Name = "AMBM"; - Colour = PIXPACK(0x00DDFF); + Colour = 0x00DDFF_rgb; Description = "Decreases ambient air temperature."; Perform = &perform; } diff --git a/src/simulation/simtools/AMBP.cpp b/src/simulation/simtools/AMBP.cpp index 2b3b5fb43..abf26175b 100644 --- a/src/simulation/simtools/AMBP.cpp +++ b/src/simulation/simtools/AMBP.cpp @@ -6,7 +6,7 @@ void SimTool::Tool_AMBP() { Identifier = "DEFAULT_TOOL_AMBP"; Name = "AMBP"; - Colour = PIXPACK(0xFFDD00); + Colour = 0xFFDD00_rgb; Description = "Increases ambient air temperature."; Perform = &perform; } diff --git a/src/simulation/simtools/COOL.cpp b/src/simulation/simtools/COOL.cpp index 651b0827d..0da7c002c 100644 --- a/src/simulation/simtools/COOL.cpp +++ b/src/simulation/simtools/COOL.cpp @@ -6,7 +6,7 @@ void SimTool::Tool_COOL() { Identifier = "DEFAULT_TOOL_COOL"; Name = "COOL"; - Colour = PIXPACK(0x00DDFF); + Colour = 0x00DDFF_rgb; Description = "Cools the targeted element."; Perform = &perform; } diff --git a/src/simulation/simtools/CYCL.cpp b/src/simulation/simtools/CYCL.cpp index 6b6bfb383..f8a0b6aee 100644 --- a/src/simulation/simtools/CYCL.cpp +++ b/src/simulation/simtools/CYCL.cpp @@ -9,7 +9,7 @@ void SimTool::Tool_CYCL() { Identifier = "DEFAULT_TOOL_CYCL"; Name = "CYCL"; - Colour = PIXPACK(0x132f5b); + Colour = 0x132f5b_rgb; Description = "Cyclone, produces swirling air currents"; Perform = &perform; } diff --git a/src/simulation/simtools/HEAT.cpp b/src/simulation/simtools/HEAT.cpp index 417295cf0..02b5ecfcd 100644 --- a/src/simulation/simtools/HEAT.cpp +++ b/src/simulation/simtools/HEAT.cpp @@ -6,7 +6,7 @@ void SimTool::Tool_HEAT() { Identifier = "DEFAULT_TOOL_HEAT"; Name = "HEAT"; - Colour = PIXPACK(0xFFDD00); + Colour = 0xFFDD00_rgb; Description = "Heats the targeted element."; Perform = &perform; } diff --git a/src/simulation/simtools/MIX.cpp b/src/simulation/simtools/MIX.cpp index 2d28e463b..2b79e15ee 100644 --- a/src/simulation/simtools/MIX.cpp +++ b/src/simulation/simtools/MIX.cpp @@ -9,7 +9,7 @@ void SimTool::Tool_MIX() { Identifier = "DEFAULT_TOOL_MIX"; Name = "MIX"; - Colour = PIXPACK(0xFFD090); + Colour = 0xFFD090_rgb; Description = "Mixes particles."; Perform = &perform; } @@ -20,7 +20,7 @@ static int perform(Simulation * sim, Particle * cpart, int x, int y, int brushX, if(!thisPart) return 0; - if(random_gen() % 100 != 0) + if(sim->rng() % 100 != 0) return 0; int distance = (int)(std::pow(strength, .5f) * 10); @@ -28,8 +28,8 @@ static int perform(Simulation * sim, Particle * cpart, int x, int y, int brushX, if(!(sim->elements[TYP(thisPart)].Properties & (TYPE_PART | TYPE_LIQUID | TYPE_GAS))) return 0; - int newX = x + (random_gen() % distance) - (distance/2); - int newY = y + (random_gen() % distance) - (distance/2); + int newX = x + (sim->rng() % distance) - (distance/2); + int newY = y + (sim->rng() % distance) - (distance/2); if(newX < 0 || newY < 0 || newX >= XRES || newY >= YRES) return 0; diff --git a/src/simulation/simtools/NGRV.cpp b/src/simulation/simtools/NGRV.cpp index 526aa8c90..c88505671 100644 --- a/src/simulation/simtools/NGRV.cpp +++ b/src/simulation/simtools/NGRV.cpp @@ -6,13 +6,13 @@ void SimTool::Tool_NGRV() { Identifier = "DEFAULT_TOOL_NGRV"; Name = "NGRV"; - Colour = PIXPACK(0xAACCFF); + Colour = 0xAACCFF_rgb; Description = "Creates a short-lasting negative gravity well."; Perform = &perform; } static int perform(Simulation * sim, Particle * cpart, int x, int y, int brushX, int brushYy, float strength) { - sim->gravmap[((y/CELL)*(XRES/CELL))+(x/CELL)] = strength*-5.0f; + sim->gravmap[((y/CELL)*XCELLS)+(x/CELL)] = strength*-5.0f; return 1; } diff --git a/src/simulation/simtools/PGRV.cpp b/src/simulation/simtools/PGRV.cpp index a9eddbb1f..d70105c34 100644 --- a/src/simulation/simtools/PGRV.cpp +++ b/src/simulation/simtools/PGRV.cpp @@ -6,13 +6,13 @@ void SimTool::Tool_PGRV() { Identifier = "DEFAULT_TOOL_PGRV"; Name = "PGRV"; - Colour = PIXPACK(0xCCCCFF); + Colour = 0xCCCCFF_rgb; Description = "Creates a short-lasting gravity well."; Perform = &perform; } static int perform(Simulation * sim, Particle * cpart, int x, int y, int brushX, int brushY, float strength) { - sim->gravmap[((y/CELL)*(XRES/CELL))+(x/CELL)] = strength*5.0f; + sim->gravmap[((y/CELL)*XCELLS)+(x/CELL)] = strength*5.0f; return 1; } diff --git a/src/simulation/simtools/VAC.cpp b/src/simulation/simtools/VAC.cpp index a41ccce85..71bb7a301 100644 --- a/src/simulation/simtools/VAC.cpp +++ b/src/simulation/simtools/VAC.cpp @@ -7,7 +7,7 @@ void SimTool::Tool_VAC() { Identifier = "DEFAULT_TOOL_VAC"; Name = "VAC"; - Colour = PIXPACK(0x303030); + Colour = 0x303030_rgb; Description = "Vacuum, reduces air pressure."; Perform = &perform; } diff --git a/src/tasks/AbandonableTask.h b/src/tasks/AbandonableTask.h index afeee9549..3c66a1613 100644 --- a/src/tasks/AbandonableTask.h +++ b/src/tasks/AbandonableTask.h @@ -1,8 +1,5 @@ -#ifndef ABANDONABLETASK_H_ -#define ABANDONABLETASK_H_ - +#pragma once #include "Task.h" - #include class AbandonableTask : public Task @@ -19,5 +16,3 @@ protected: void doWork_wrapper() override; bool thAbandoned; }; - -#endif /* ABANDONABLETASK_H_ */ diff --git a/src/tasks/Task.h b/src/tasks/Task.h index 7398bc6c9..96ddff174 100644 --- a/src/tasks/Task.h +++ b/src/tasks/Task.h @@ -1,7 +1,4 @@ -#ifndef TASK_H_ -#define TASK_H_ -#include "Config.h" - +#pragma once #include "common/String.h" #include #include @@ -50,5 +47,3 @@ protected: virtual void notifyStatusMain(); virtual void notifyDoneMain(); }; - -#endif /* TASK_H_ */ diff --git a/src/tasks/TaskListener.h b/src/tasks/TaskListener.h index 13df07c36..d2004aa7d 100644 --- a/src/tasks/TaskListener.h +++ b/src/tasks/TaskListener.h @@ -1,5 +1,4 @@ -#ifndef TASKLISTENER_H_ -#define TASKLISTENER_H_ +#pragma once class Task; class TaskListener { @@ -10,5 +9,3 @@ public: virtual void NotifyStatus(Task * task) {} virtual ~TaskListener() {} }; - -#endif /* TASK_H_ */ diff --git a/src/tasks/TaskWindow.cpp b/src/tasks/TaskWindow.cpp index 12d941c97..448cf309d 100644 --- a/src/tasks/TaskWindow.cpp +++ b/src/tasks/TaskWindow.cpp @@ -86,10 +86,10 @@ void TaskWindow::OnTick(float dt) void TaskWindow::OnDraw() { Graphics * g = GetGraphics(); - g->clearrect(Position.X-2, Position.Y-2, Size.X+3, Size.Y+3); - g->drawrect(Position.X, Position.Y, Size.X, Size.Y, 255, 255, 255, 255); + g->DrawFilledRect(RectSized(Position - Vec2{ 1, 1 }, Size + Vec2{ 2, 2 }), 0x000000_rgb); + g->DrawRect(RectSized(Position, Size), 0xFFFFFF_rgb); - g->draw_line(Position.X, Position.Y + Size.Y-17, Position.X + Size.X - 1, Position.Y + Size.Y-17, 255, 255, 255, 255); + g->DrawLine(Position + Vec2{ 0, Size.Y-17 }, Position + Vec2{ Size.X - 1, Size.Y-17 }, 0xFFFFFF_rgb); ui::Colour progressBarColour = style::Colour::WarningTitle; @@ -101,7 +101,7 @@ void TaskWindow::OnDraw() progress = 100; float size = float(Size.X-4)*(float(progress)/100.0f); // TIL... size = std::min(std::max(size, 0.0f), float(Size.X-4)); - g->fillrect(Position.X + 2, Position.Y + Size.Y-15, int(size), 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); + g->DrawFilledRect(RectSized(Position + Vec2{ 2, Size.Y-15 }, Vec2{ int(size), 13 }), progressBarColour.NoAlpha()); } } else { int size = 40, rsize = 0; @@ -111,16 +111,13 @@ void TaskWindow::OnDraw() size = (Size.X-4)-int(position)+1; rsize = 40-size; } - g->fillrect(Position.X + 2 + int(position), Position.Y + Size.Y-15, size, 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); + g->DrawFilledRect(RectSized(Position + Vec2{ 2 + int(position), Size.Y-15 }, Vec2{ size, 13 }), progressBarColour.NoAlpha()); if(rsize) { - g->fillrect(Position.X + 2, Position.Y + Size.Y-15, rsize, 13, progressBarColour.Red, progressBarColour.Green, progressBarColour.Blue, 255); + g->DrawFilledRect(RectSized(Position + Vec2{ 2, Size.Y-15 }, Vec2{ rsize, 13 }), progressBarColour.NoAlpha()); } } - if(progress<50) - g->drawtext(Position.X + ((Size.X-Graphics::textwidth(progressStatus))/2), Position.Y + Size.Y-13, progressStatus, 255, 255, 255, 255); - else - g->drawtext(Position.X + ((Size.X-Graphics::textwidth(progressStatus))/2), Position.Y + Size.Y-13, progressStatus, 0, 0, 0, 255); + g->BlendText(Position + Vec2{ ((Size.X-(Graphics::TextSize(progressStatus).X - 1))/2), Size.Y-13 }, progressStatus, progress<50 ? 0xFFFFFF_rgb .WithAlpha(255) : 0x000000_rgb .WithAlpha(255)); } TaskWindow::~TaskWindow() { diff --git a/src/tasks/TaskWindow.h b/src/tasks/TaskWindow.h index 9c2cc3f31..d70c0a6ce 100644 --- a/src/tasks/TaskWindow.h +++ b/src/tasks/TaskWindow.h @@ -1,6 +1,4 @@ -#ifndef TASKWINDOW_H_ -#define TASKWINDOW_H_ - +#pragma once #include "gui/interface/Window.h" #include "tasks/TaskListener.h" @@ -30,5 +28,3 @@ public: void Exit(); virtual ~TaskWindow(); }; - -#endif /* TASKWINDOW_H_ */ diff --git a/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20221223184203.wrap deleted file mode 100644 index fe1ae7b8f..000000000 --- a/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20221223184203.zip -source_hash = d9fd15fd80ceaf1796c0501df3c35ecd58706fc1cba64a5739cee365e1172d40 diff --git a/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..8a2d9cbd4 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-aarch64-android-bionic-static-debug-v20230205154205.zip +source_hash = d7bd10c6939a779dd8e28783519cc8e05dea201ae3bd9e7edd050ee30430a670 diff --git a/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20221223184203.wrap deleted file mode 100644 index b01278271..000000000 --- a/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20221223184203.zip -source_hash = 8af13fe42816067199515dc41e81c565ea360a98bdc5d4ddabe10fbdff0d1a52 diff --git a/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20230205154205.wrap new file mode 100644 index 000000000..f92574164 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-aarch64-android-bionic-static-release-v20230205154205.zip +source_hash = 0f42b3864f0bd20079d8a89136357cd679962ccaee957c1a7fa5f8f33df6781e diff --git a/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20221223184203.wrap deleted file mode 100644 index 5b823c336..000000000 --- a/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20221223184203.zip -source_hash = 0793a90a604b157751fb5b4b62828d98c28770389db73e9b100c33f21ed1d4f2 diff --git a/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..c9fd1792c --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-aarch64-darwin-macos-static-debug-v20230205154205.zip +source_hash = 330af580a037604519bea913d457d343aacb23b647b2842692cc9ffc53402801 diff --git a/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20221223184203.wrap deleted file mode 100644 index 9fbbe3b6e..000000000 --- a/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20221223184203.zip -source_hash = 19b44179d6b6253d7fb21432a64563dc3640bfa28af5fa427912dcf582169aa3 diff --git a/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20230205154205.wrap new file mode 100644 index 000000000..1113c4918 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-aarch64-darwin-macos-static-release-v20230205154205.zip +source_hash = 561c58811dd6567512884e903018e047b235198efaf3c138e466e397aa4a1d14 diff --git a/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-debug-v20221223184203.wrap deleted file mode 100644 index f6f58072e..000000000 --- a/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-arm-android-bionic-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-arm-android-bionic-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-arm-android-bionic-static-debug-v20221223184203.zip -source_hash = e22de49e2614738f6c2f1d0e3bbb6c7b2f18e07d9bbd8bc6215438d6604c2aef diff --git a/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..8db87a7e5 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-arm-android-bionic-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-arm-android-bionic-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-arm-android-bionic-static-debug-v20230205154205.zip +source_hash = 8611687c92596c64486d7280c5ceb106e5eff4352b4438b4050046286f8ad0fa diff --git a/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-release-v20221223184203.wrap deleted file mode 100644 index 3c131fa74..000000000 --- a/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-arm-android-bionic-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-arm-android-bionic-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-arm-android-bionic-static-release-v20221223184203.zip -source_hash = 8586c04b6443303bb30f3410c17d15bc3132a5e949b97bd99406b214e894226e diff --git a/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-release-v20230205154205.wrap new file mode 100644 index 000000000..707a4ac2b --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-arm-android-bionic-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-arm-android-bionic-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-arm-android-bionic-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-arm-android-bionic-static-release-v20230205154205.zip +source_hash = db7ddda648aade2b390e0f03a7b4ca61577cc29c01323668b50ecd89aeff12da diff --git a/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-debug-v20221223184203.wrap deleted file mode 100644 index 81179f043..000000000 --- a/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86-android-bionic-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86-android-bionic-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86-android-bionic-static-debug-v20221223184203.zip -source_hash = 5a02248b6655208748a27aba50f2d6b787690c16048d2d9ab5f6a559a3bf5841 diff --git a/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..d22b3d98b --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86-android-bionic-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86-android-bionic-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86-android-bionic-static-debug-v20230205154205.zip +source_hash = 6419b422a5f79509c7ca1f08a3ccaa97a7475db3ded99b9073a2b682819758f3 diff --git a/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-release-v20221223184203.wrap deleted file mode 100644 index 7b88af74e..000000000 --- a/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86-android-bionic-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86-android-bionic-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86-android-bionic-static-release-v20221223184203.zip -source_hash = 9667f7fb986dfe9d87a567d8a59c35d7e464ab53ad4b496109289e5ae8915e0c diff --git a/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-release-v20230205154205.wrap new file mode 100644 index 000000000..736f23505 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86-android-bionic-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86-android-bionic-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86-android-bionic-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86-android-bionic-static-release-v20230205154205.zip +source_hash = ac6e3a6bb88f2bd66aa9f6bb29d0675feac6ad284145849feac2311b43b80abc diff --git a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20221223184203.wrap deleted file mode 100644 index ace822242..000000000 --- a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20221223184203.zip -source_hash = a38fc82a77c3855f150a469d086bd4827c5da76e5434bb5b19b1834a527ebc49 diff --git a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20230205154205.wrap new file mode 100644 index 000000000..01a6ff143 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86-windows-msvc-dynamic-debug-v20230205154205.zip +source_hash = 31bcb99b756909fadb6dc4b66a347435a040e384b4418a2ae4739ebb7383c6dd diff --git a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20221223184203.wrap deleted file mode 100644 index ba07f005c..000000000 --- a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20221223184203.zip -source_hash = 250c72b6cde59f73cce618a976f22ba49421af8acf14674e9d81aeeb9c8f0443 diff --git a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20230205154205.wrap new file mode 100644 index 000000000..d9e3df082 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86-windows-msvc-dynamic-release-v20230205154205.zip +source_hash = a17f71f9cee99a69abbb54bd0e0f01e9c48e79e1e26d73334d1690c73afac0c8 diff --git a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20221223184203.wrap deleted file mode 100644 index 782bca160..000000000 --- a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20221223184203.zip -source_hash = 99966e124c6c4b7e93d43b885dfbe39ea88fcb2cbd20239b2ced643283a34cdf diff --git a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..3a41ea543 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86-windows-msvc-static-debug-v20230205154205.zip +source_hash = e5e073d65c1e26dbd5d854ec865917d1a14298cddc72474cc4a1e4830ee27969 diff --git a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-release-v20221223184203.wrap deleted file mode 100644 index 466548b95..000000000 --- a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86-windows-msvc-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86-windows-msvc-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86-windows-msvc-static-release-v20221223184203.zip -source_hash = 032ace429309624e7cb9f49378000a15162acd79adc7c5b085d5c6a9fb0a7bd3 diff --git a/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-release-v20230205154205.wrap new file mode 100644 index 000000000..5dab494a6 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86-windows-msvc-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86-windows-msvc-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86-windows-msvc-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86-windows-msvc-static-release-v20230205154205.zip +source_hash = 018213f1efab1a29ab32daba6298b9e54e612617bb3d7a59438e2489f20f83e2 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20221223184203.wrap deleted file mode 100644 index b6a934d4b..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20221223184203.zip -source_hash = d96330e5057f3a8f9a0d2edfabd63e1ad2b86733bd43a0e1c290ca59e6edc8e5 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..1561de549 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-android-bionic-static-debug-v20230205154205.zip +source_hash = 8a06f5eb59668ed3de4dfb706f6e403952e4730609898f218a38f1f6ee0971b9 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20221223184203.wrap deleted file mode 100644 index 2cb896906..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20221223184203.zip -source_hash = e62860adc1250ac67406dede58d9f4171cd44a4bf5a4ff0a56cf606650c36178 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20230205154205.wrap new file mode 100644 index 000000000..a4e45064a --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-android-bionic-static-release-v20230205154205.zip +source_hash = 027bf628aa18454c99622e792aa751c1979c1e564482d59fac4125515767e870 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20221223184203.wrap deleted file mode 100644 index cdda98fc2..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20221223184203.zip -source_hash = 45a09ba7f3a3a3560e81b045973b7998561604bbbf4cca270d3e5861452efafa diff --git a/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..7b6eca885 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-darwin-macos-static-debug-v20230205154205.zip +source_hash = 87890379194b23bad79b44cac51a3e32c1a8889e480d49220e912ec02357ed9c diff --git a/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20221223184203.wrap deleted file mode 100644 index 0b3eb11a9..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20221223184203.zip -source_hash = caa92a0ff2dda2f3defa95d01196e7d415bfd246bb8c31a949035bf246a91c6b diff --git a/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20230205154205.wrap new file mode 100644 index 000000000..7b70a0bf7 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-darwin-macos-static-release-v20230205154205.zip +source_hash = ed0e62e82dd76572dd6af06577524fa67c7907470272c9003d73f9f154f063b5 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20221223184203.wrap deleted file mode 100644 index 4f5d5a2b0..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20221223184203.zip -source_hash = b5a4a7503195439b696d9763ced117aa281c6b5cbf04191e33e90189fb5b7baf diff --git a/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..2571167ca --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-linux-gnu-static-debug-v20230205154205.zip +source_hash = 41357dc681f6c761f4cf209ceedb2c6cb38e6dc48380ec48de0dc2af2ebfe39c diff --git a/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20221223184203.wrap deleted file mode 100644 index c9b5bf7cd..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20221223184203.zip -source_hash = ca8d14e51fd14934435b4cdb58c9c474c335770e48b6c0cd5df24e411558a607 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20230205154205.wrap new file mode 100644 index 000000000..f404a01de --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-linux-gnu-static-release-v20230205154205.zip +source_hash = d32c6efeb82a0a691c84cd1fde7fb54266e720a2bb3300ed2e25ad9c68d15985 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20221223184203.wrap deleted file mode 100644 index 0cdb83cb0..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20221223184203.zip -source_hash = 9b6c40b30a9a3851a393eea6e070845d447a1d09812e31fe2da721854b6d76ad diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20230205154205.wrap new file mode 100644 index 000000000..8654fae77 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-debug-v20230205154205.zip +source_hash = 979b021cdb0a96a5edf49c8aa4790f630131eaa5f25540ed3539d89d6fe637ae diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20221223184203.wrap deleted file mode 100644 index 5c7a82ab5..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20221223184203.zip -source_hash = 57aa50ca1fd1d98e781ec678e5655178ff3350d0b6e44a985f04127ba49ffb45 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20230205154205.wrap new file mode 100644 index 000000000..0fbbd376e --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-windows-mingw-dynamic-release-v20230205154205.zip +source_hash = cb2ded593f9e470cdd8d8f3d49a759d9f751c080faf6750f2d076a5559cf3ee0 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20221223184203.wrap deleted file mode 100644 index 203960b7a..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20221223184203.zip -source_hash = 1e5e27cc50993fad984863e0b6fb3592bdbc901fa0f902629c9360a7fed74163 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..12ed3f67b --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-windows-mingw-static-debug-v20230205154205.zip +source_hash = 9a47742b7e22491b3350f686ee149236a425a555a91abc4b0356f3f11432ad03 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20221223184203.wrap deleted file mode 100644 index 0cc141c90..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20221223184203.zip -source_hash = e0a65ebcdf1133876d71a9f17d882ab25a8cbabff5e487e0f34ff7a65a8991e6 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20230205154205.wrap new file mode 100644 index 000000000..85782275f --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-windows-mingw-static-release-v20230205154205.zip +source_hash = 3944d6b5cb26f53785b52b33c542bdc9aa10a5443b0be98f402a315dc4a113bf diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20221223184203.wrap deleted file mode 100644 index 367653a45..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20221223184203.zip -source_hash = b2157b9881e3ec6eceb0e40244b2ff22696c899ec7aeea29d3633ea7327558b7 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20230205154205.wrap new file mode 100644 index 000000000..c3ccf4cfd --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-debug-v20230205154205.zip +source_hash = 21603a4a04ce066b86683a3535bffaaa1327181b06c4f8fe7d094faba2dd2184 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20221223184203.wrap deleted file mode 100644 index fe22a6c90..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20221223184203.zip -source_hash = be0f238ee44ab45498779dc0037f23aa6679294a38d6beec48079eb4c27ca477 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20230205154205.wrap new file mode 100644 index 000000000..568045c80 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-windows-msvc-dynamic-release-v20230205154205.zip +source_hash = 748751bdc031be172d731ee2f05a4cf33419858b9e896fed3a8e3753604eadfe diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20221223184203.wrap deleted file mode 100644 index 869000e71..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20221223184203.zip -source_hash = f34e7decb5b753295a0074f6e8aafccc6fe3d2044ce19bb1de301b69756cbf2b diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20230205154205.wrap new file mode 100644 index 000000000..83d809a5e --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-windows-msvc-static-debug-v20230205154205.zip +source_hash = 7e9c85f54dc03c78f8e221c46a4ae4b9409239f949393cd978711f85279448e4 diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20221223184203.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20221223184203.wrap deleted file mode 100644 index 1a9de99dd..000000000 --- a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20221223184203.wrap +++ /dev/null @@ -1,6 +0,0 @@ -[wrap-file] -directory = tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20221223184203 - -source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20221223184203/tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20221223184203.zip -source_filename = tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20221223184203.zip -source_hash = 61abc8e8986f64aa6bbe43b52a806dc98005cbc7bab35bfbcd5c8cdb1becd24e diff --git a/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20230205154205.wrap b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20230205154205.wrap new file mode 100644 index 000000000..1af6cfad8 --- /dev/null +++ b/subprojects/tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20230205154205.wrap @@ -0,0 +1,6 @@ +[wrap-file] +directory = tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20230205154205 + +source_url = https://github.com/The-Powder-Toy/tpt-libs/releases/download/v20230205154205/tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20230205154205.zip +source_filename = tpt-libs-prebuilt-x86_64-windows-msvc-static-release-v20230205154205.zip +source_hash = dbfa2ed0f2a2ffe4be1c82915145cdb28480be134df835d89a7aa0f7690140af